##// 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
@@ -1,96 +1,95 b''
1 """A kernel manager relating notebooks and kernels
1 """A kernel manager relating notebooks and kernels
2
2
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2013 The IPython Development Team
9 # Copyright (C) 2013 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 from tornado import web
19 from tornado import web
20
20
21 from IPython.kernel.multikernelmanager import MultiKernelManager
21 from IPython.kernel.multikernelmanager import MultiKernelManager
22 from IPython.utils.traitlets import (
22 from IPython.utils.traitlets import (
23 Dict, List, Unicode,
23 Dict, List, Unicode,
24 )
24 )
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Classes
27 # Classes
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30
30
31 class MappingKernelManager(MultiKernelManager):
31 class MappingKernelManager(MultiKernelManager):
32 """A KernelManager that handles notebook mapping and HTTP error handling"""
32 """A KernelManager that handles notebook mapping and HTTP error handling"""
33
33
34 def _kernel_manager_class_default(self):
34 def _kernel_manager_class_default(self):
35 return "IPython.kernel.ioloop.IOLoopKernelManager"
35 return "IPython.kernel.ioloop.IOLoopKernelManager"
36
36
37 kernel_argv = List(Unicode)
37 kernel_argv = List(Unicode)
38 kernels = []
38 kernels = []
39
39
40 #-------------------------------------------------------------------------
40 #-------------------------------------------------------------------------
41 # Methods for managing kernels and sessions
41 # Methods for managing kernels and sessions
42 #-------------------------------------------------------------------------
42 #-------------------------------------------------------------------------
43
43
44 def _handle_kernel_died(self, kernel_id):
44 def _handle_kernel_died(self, kernel_id):
45 """notice that a kernel died"""
45 """notice that a kernel died"""
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)
63 self.log.info("Kernel started: %s" % kernel_id)
62 self.log.info("Kernel started: %s" % kernel_id)
64 self.log.debug("Kernel args: %r" % kwargs)
63 self.log.debug("Kernel args: %r" % kwargs)
65 # register callback for failed auto-restart
64 # register callback for failed auto-restart
66 self.add_restart_callback(kernel_id,
65 self.add_restart_callback(kernel_id,
67 lambda : self._handle_kernel_died(kernel_id),
66 lambda : self._handle_kernel_died(kernel_id),
68 'dead',
67 'dead',
69 )
68 )
70 else:
69 else:
71 self.log.info("Using existing kernel: %s" % kernel_id)
70 self.log.info("Using existing kernel: %s" % kernel_id)
72
71
73 return kernel_id
72 return kernel_id
74
73
75 def shutdown_kernel(self, kernel_id, now=False):
74 def shutdown_kernel(self, kernel_id, now=False):
76 """Shutdown a kernel by kernel_id"""
75 """Shutdown a kernel by kernel_id"""
77 i = 0
76 i = 0
78 for kernel in self.kernels:
77 for kernel in self.kernels:
79 if kernel['id'] == kernel_id:
78 if kernel['id'] == kernel_id:
80 del self.kernels[i]
79 del self.kernels[i]
81 i = i+1
80 i = i+1
82 super(MappingKernelManager, self).shutdown_kernel(kernel_id, now=now)
81 super(MappingKernelManager, self).shutdown_kernel(kernel_id, now=now)
83
82
84 def kernel_model(self, kernel_id, ws_url):
83 def kernel_model(self, kernel_id, ws_url):
85 model = {"id":kernel_id, "ws_url": ws_url}
84 model = {"id":kernel_id, "ws_url": ws_url}
86 self.kernels.append(model)
85 self.kernels.append(model)
87 return model
86 return model
88
87
89 def list_kernels(self):
88 def list_kernels(self):
90 return self.kernels
89 return self.kernels
91
90
92 # override _check_kernel_id to raise 404 instead of KeyError
91 # override _check_kernel_id to raise 404 instead of KeyError
93 def _check_kernel_id(self, kernel_id):
92 def _check_kernel_id(self, kernel_id):
94 """Check a that a kernel_id exists and raise 404 if not."""
93 """Check a that a kernel_id exists and raise 404 if not."""
95 if kernel_id not in self:
94 if kernel_id not in self:
96 raise web.HTTPError(404, u'Kernel does not exist: %s' % kernel_id)
95 raise web.HTTPError(404, u'Kernel does not exist: %s' % kernel_id)
@@ -1,104 +1,106 b''
1 """Tornado handlers for the sessions web service.
1 """Tornado handlers for the sessions web service.
2
2
3 Authors:
3 Authors:
4
4
5 * Zach Sailer
5 * Zach Sailer
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2013 The IPython Development Team
9 # Copyright (C) 2013 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 from tornado import web
19 from tornado import web
20
20
21 from zmq.utils import jsonapi
21 from zmq.utils import jsonapi
22
22
23 from IPython.utils.jsonutil import date_default
23 from IPython.utils.jsonutil import date_default
24 from ...base.handlers import IPythonHandler
24 from ...base.handlers import IPythonHandler
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Session web service handlers
27 # Session web service handlers
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30
30
31
32 class SessionRootHandler(IPythonHandler):
31 class SessionRootHandler(IPythonHandler):
33
32
34 @web.authenticated
33 @web.authenticated
35 def get(self):
34 def get(self):
36 sm = self.session_manager
35 sm = self.session_manager
37 nbm = self.notebook_manager
36 nbm = self.notebook_manager
38 km = self.kernel_manager
37 km = self.kernel_manager
39 sessions = sm.list_sessions()
38 sessions = sm.list_sessions()
40 self.finish(jsonapi.dumps(sessions))
39 self.finish(jsonapi.dumps(sessions))
41
40
42 @web.authenticated
41 @web.authenticated
43 def post(self):
42 def post(self):
44 sm = self.session_manager
43 sm = self.session_manager
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):
57
63
58 SUPPORTED_METHODS = ('GET', 'PATCH', 'DELETE')
64 SUPPORTED_METHODS = ('GET', 'PATCH', 'DELETE')
59
65
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
80 def delete(self, session_id):
85 def delete(self, session_id):
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
90
95
91 #-----------------------------------------------------------------------------
96 #-----------------------------------------------------------------------------
92 # URL to handler mappings
97 # URL to handler mappings
93 #-----------------------------------------------------------------------------
98 #-----------------------------------------------------------------------------
94
99
95 _session_id_regex = r"(?P<session_id>\w+-\w+-\w+-\w+-\w+)"
100 _session_id_regex = r"(?P<session_id>\w+-\w+-\w+-\w+-\w+)"
96
101
97 default_handlers = [
102 default_handlers = [
98 (r"api/sessions/%s" % _session_id_regex, SessionHandler),
103 (r"api/sessions/%s" % _session_id_regex, SessionHandler),
99 (r"api/sessions", SessionRootHandler)
104 (r"api/sessions", SessionRootHandler)
100 ]
105 ]
101
106
102
103
104
@@ -1,97 +1,170 b''
1 """A base class session manager.
1 """A base class session manager.
2
2
3 Authors:
3 Authors:
4
4
5 * Zach Sailer
5 * Zach Sailer
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2013 The IPython Development Team
9 # Copyright (C) 2013 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
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
24 from IPython.config.configurable import LoggingConfigurable
25 from IPython.config.configurable import LoggingConfigurable
25 from IPython.nbformat import current
26 from IPython.nbformat import current
26 from IPython.utils.traitlets import List, Dict, Unicode, TraitError
27 from IPython.utils.traitlets import List, Dict, Unicode, TraitError
27
28
28 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
29 # Classes
30 # Classes
30 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
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
@@ -1,527 +1,525 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Kernel
9 // Kernel
10 //============================================================================
10 //============================================================================
11
11
12 /**
12 /**
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 * @submodule Kernel
15 * @submodule Kernel
16 */
16 */
17
17
18 var IPython = (function (IPython) {
18 var IPython = (function (IPython) {
19
19
20 var utils = IPython.utils;
20 var utils = IPython.utils;
21
21
22 // Initialization and connection.
22 // Initialization and connection.
23 /**
23 /**
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') {
40 this.WebSocket = WebSocket;
39 this.WebSocket = WebSocket;
41 } else if (typeof(MozWebSocket) !== 'undefined') {
40 } else if (typeof(MozWebSocket) !== 'undefined') {
42 this.WebSocket = MozWebSocket;
41 this.WebSocket = MozWebSocket;
43 } else {
42 } else {
44 alert('Your browser does not have WebSocket support, please try Chrome, Safari or Firefox β‰₯ 6. Firefox 4 and 5 are also supported by you have to enable WebSockets in about:config.');
43 alert('Your browser does not have WebSocket support, please try Chrome, Safari or Firefox β‰₯ 6. Firefox 4 and 5 are also supported by you have to enable WebSockets in about:config.');
45 };
44 };
46 this.bind_events();
45 this.bind_events();
47 };
46 };
48
47
49
48
50 Kernel.prototype._get_msg = function (msg_type, content) {
49 Kernel.prototype._get_msg = function (msg_type, content) {
51 var msg = {
50 var msg = {
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 : {},
59 content : content,
58 content : content,
60 parent_header : {}
59 parent_header : {}
61 };
60 };
62 return msg;
61 return msg;
63 };
62 };
64
63
65 Kernel.prototype.bind_events = function() {
64 Kernel.prototype.bind_events = function() {
66 var that = this;
65 var that = this;
67 $([IPython.events]).on('send_input_reply.Kernel', function(evt, data) {
66 $([IPython.events]).on('send_input_reply.Kernel', function(evt, data) {
68 that.send_input_reply(data);
67 that.send_input_reply(data);
69 });
68 });
70 }
69 }
71
70
72 /**
71 /**
73 * Start the Python kernel
72 * Start the Python kernel
74 * @method start
73 * @method start
75 */
74 */
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;
83 $.post(url,
81 $.post(url,
84 $.proxy(that._kernel_started,that),
82 $.proxy(that._kernel_started,that),
85 'json'
83 'json'
86 );
84 );
87 };
85 };
88 };
86 };
89
87
90 /**
88 /**
91 * Restart the python kernel.
89 * Restart the python kernel.
92 *
90 *
93 * Emit a 'status_restarting.Kernel' event with
91 * Emit a 'status_restarting.Kernel' event with
94 * the current object as parameter
92 * the current object as parameter
95 *
93 *
96 * @method restart
94 * @method restart
97 */
95 */
98 Kernel.prototype.restart = function () {
96 Kernel.prototype.restart = function () {
99 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
97 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
100 var that = this;
98 var that = this;
101 if (this.running) {
99 if (this.running) {
102 this.stop_channels();
100 this.stop_channels();
103 var url = this.kernel_url + "/restart";
101 var url = this.kernel_url + "/restart";
104 $.post(url,
102 $.post(url,
105 $.proxy(that._kernel_started, that),
103 $.proxy(that._kernel_started, that),
106 'json'
104 'json'
107 );
105 );
108 };
106 };
109 };
107 };
110
108
111
109
112 Kernel.prototype._kernel_started = function (json) {
110 Kernel.prototype._kernel_started = function (json) {
113 console.log("Kernel started: ", json.id);
111 console.log("Kernel started: ", json.id);
114 this.running = true;
112 this.running = true;
115 this.kernel_id = json.id;
113 this.kernel_id = json.id;
116 var ws_url = json.ws_url;
114 var ws_url = json.ws_url;
117 if (ws_url.match(/wss?:\/\//) == null) {
115 if (ws_url.match(/wss?:\/\//) == null) {
118 // trailing 's' in https will become wss for secure web sockets
116 // trailing 's' in https will become wss for secure web sockets
119 prot = location.protocol.replace('http', 'ws') + "//";
117 prot = location.protocol.replace('http', 'ws') + "//";
120 ws_url = prot + location.host + ws_url;
118 ws_url = prot + location.host + ws_url;
121 };
119 };
122 this.ws_url = ws_url;
120 this.ws_url = ws_url;
123 this.kernel_url = this.base_url + "/" + this.kernel_id;
121 this.kernel_url = this.base_url + "/" + this.kernel_id;
124 this.start_channels();
122 this.start_channels();
125 };
123 };
126
124
127
125
128 Kernel.prototype._websocket_closed = function(ws_url, early) {
126 Kernel.prototype._websocket_closed = function(ws_url, early) {
129 this.stop_channels();
127 this.stop_channels();
130 $([IPython.events]).trigger('websocket_closed.Kernel',
128 $([IPython.events]).trigger('websocket_closed.Kernel',
131 {ws_url: ws_url, kernel: this, early: early}
129 {ws_url: ws_url, kernel: this, early: early}
132 );
130 );
133 };
131 };
134
132
135 /**
133 /**
136 * Start the `shell`and `iopub` channels.
134 * Start the `shell`and `iopub` channels.
137 * Will stop and restart them if they already exist.
135 * Will stop and restart them if they already exist.
138 *
136 *
139 * @method start_channels
137 * @method start_channels
140 */
138 */
141 Kernel.prototype.start_channels = function () {
139 Kernel.prototype.start_channels = function () {
142 var that = this;
140 var that = this;
143 this.stop_channels();
141 this.stop_channels();
144 var ws_url = this.ws_url + this.kernel_url;
142 var ws_url = this.ws_url + this.kernel_url;
145 console.log("Starting WebSockets:", ws_url);
143 console.log("Starting WebSockets:", ws_url);
146 this.shell_channel = new this.WebSocket(ws_url + "/shell");
144 this.shell_channel = new this.WebSocket(ws_url + "/shell");
147 this.stdin_channel = new this.WebSocket(ws_url + "/stdin");
145 this.stdin_channel = new this.WebSocket(ws_url + "/stdin");
148 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
146 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
149
147
150 var already_called_onclose = false; // only alert once
148 var already_called_onclose = false; // only alert once
151 var ws_closed_early = function(evt){
149 var ws_closed_early = function(evt){
152 if (already_called_onclose){
150 if (already_called_onclose){
153 return;
151 return;
154 }
152 }
155 already_called_onclose = true;
153 already_called_onclose = true;
156 if ( ! evt.wasClean ){
154 if ( ! evt.wasClean ){
157 that._websocket_closed(ws_url, true);
155 that._websocket_closed(ws_url, true);
158 }
156 }
159 };
157 };
160 var ws_closed_late = function(evt){
158 var ws_closed_late = function(evt){
161 if (already_called_onclose){
159 if (already_called_onclose){
162 return;
160 return;
163 }
161 }
164 already_called_onclose = true;
162 already_called_onclose = true;
165 if ( ! evt.wasClean ){
163 if ( ! evt.wasClean ){
166 that._websocket_closed(ws_url, false);
164 that._websocket_closed(ws_url, false);
167 }
165 }
168 };
166 };
169 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
167 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
170 for (var i=0; i < channels.length; i++) {
168 for (var i=0; i < channels.length; i++) {
171 channels[i].onopen = $.proxy(this._ws_opened, this);
169 channels[i].onopen = $.proxy(this._ws_opened, this);
172 channels[i].onclose = ws_closed_early;
170 channels[i].onclose = ws_closed_early;
173 }
171 }
174 // switch from early-close to late-close message after 1s
172 // switch from early-close to late-close message after 1s
175 setTimeout(function() {
173 setTimeout(function() {
176 for (var i=0; i < channels.length; i++) {
174 for (var i=0; i < channels.length; i++) {
177 if (channels[i] !== null) {
175 if (channels[i] !== null) {
178 channels[i].onclose = ws_closed_late;
176 channels[i].onclose = ws_closed_late;
179 }
177 }
180 }
178 }
181 }, 1000);
179 }, 1000);
182 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply, this);
180 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply, this);
183 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply, this);
181 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply, this);
184 this.stdin_channel.onmessage = $.proxy(this._handle_input_request, this);
182 this.stdin_channel.onmessage = $.proxy(this._handle_input_request, this);
185 };
183 };
186
184
187 /**
185 /**
188 * Handle a websocket entering the open state
186 * Handle a websocket entering the open state
189 * sends session and cookie authentication info as first message.
187 * sends session and cookie authentication info as first message.
190 * Once all sockets are open, signal the Kernel.status_started event.
188 * Once all sockets are open, signal the Kernel.status_started event.
191 * @method _ws_opened
189 * @method _ws_opened
192 */
190 */
193 Kernel.prototype._ws_opened = function (evt) {
191 Kernel.prototype._ws_opened = function (evt) {
194 // send the session id so the Session object Python-side
192 // send the session id so the Session object Python-side
195 // has the same identity
193 // has the same identity
196 evt.target.send(this.session_id + ':' + document.cookie);
194 evt.target.send(this.session_id + ':' + document.cookie);
197
195
198 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
196 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
199 for (var i=0; i < channels.length; i++) {
197 for (var i=0; i < channels.length; i++) {
200 // if any channel is not ready, don't trigger event.
198 // if any channel is not ready, don't trigger event.
201 if ( !channels[i].readyState ) return;
199 if ( !channels[i].readyState ) return;
202 }
200 }
203 // all events ready, trigger started event.
201 // all events ready, trigger started event.
204 $([IPython.events]).trigger('status_started.Kernel', {kernel: this});
202 $([IPython.events]).trigger('status_started.Kernel', {kernel: this});
205 };
203 };
206
204
207 /**
205 /**
208 * Stop the websocket channels.
206 * Stop the websocket channels.
209 * @method stop_channels
207 * @method stop_channels
210 */
208 */
211 Kernel.prototype.stop_channels = function () {
209 Kernel.prototype.stop_channels = function () {
212 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
210 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
213 for (var i=0; i < channels.length; i++) {
211 for (var i=0; i < channels.length; i++) {
214 if ( channels[i] !== null ) {
212 if ( channels[i] !== null ) {
215 channels[i].onclose = function (evt) {};
213 channels[i].onclose = function (evt) {};
216 channels[i].close();
214 channels[i].close();
217 }
215 }
218 };
216 };
219 this.shell_channel = this.iopub_channel = this.stdin_channel = null;
217 this.shell_channel = this.iopub_channel = this.stdin_channel = null;
220 };
218 };
221
219
222 // Main public methods.
220 // Main public methods.
223
221
224 /**
222 /**
225 * Get info on object asynchronoulsy
223 * Get info on object asynchronoulsy
226 *
224 *
227 * @async
225 * @async
228 * @param objname {string}
226 * @param objname {string}
229 * @param callback {dict}
227 * @param callback {dict}
230 * @method object_info_request
228 * @method object_info_request
231 *
229 *
232 * @example
230 * @example
233 *
231 *
234 * When calling this method pass a callbacks structure of the form:
232 * When calling this method pass a callbacks structure of the form:
235 *
233 *
236 * callbacks = {
234 * callbacks = {
237 * 'object_info_reply': object_info_reply_callback
235 * 'object_info_reply': object_info_reply_callback
238 * }
236 * }
239 *
237 *
240 * The `object_info_reply_callback` will be passed the content object of the
238 * The `object_info_reply_callback` will be passed the content object of the
241 *
239 *
242 * `object_into_reply` message documented in
240 * `object_into_reply` message documented in
243 * [IPython dev documentation](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
241 * [IPython dev documentation](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
244 */
242 */
245 Kernel.prototype.object_info_request = function (objname, callbacks) {
243 Kernel.prototype.object_info_request = function (objname, callbacks) {
246 if(typeof(objname)!=null && objname!=null)
244 if(typeof(objname)!=null && objname!=null)
247 {
245 {
248 var content = {
246 var content = {
249 oname : objname.toString(),
247 oname : objname.toString(),
250 detail_level : 0,
248 detail_level : 0,
251 };
249 };
252 var msg = this._get_msg("object_info_request", content);
250 var msg = this._get_msg("object_info_request", content);
253 this.shell_channel.send(JSON.stringify(msg));
251 this.shell_channel.send(JSON.stringify(msg));
254 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
252 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
255 return msg.header.msg_id;
253 return msg.header.msg_id;
256 }
254 }
257 return;
255 return;
258 }
256 }
259
257
260 /**
258 /**
261 * Execute given code into kernel, and pass result to callback.
259 * Execute given code into kernel, and pass result to callback.
262 *
260 *
263 * TODO: document input_request in callbacks
261 * TODO: document input_request in callbacks
264 *
262 *
265 * @async
263 * @async
266 * @method execute
264 * @method execute
267 * @param {string} code
265 * @param {string} code
268 * @param [callbacks] {Object} With the optional following keys
266 * @param [callbacks] {Object} With the optional following keys
269 * @param callbacks.'execute_reply' {function}
267 * @param callbacks.'execute_reply' {function}
270 * @param callbacks.'output' {function}
268 * @param callbacks.'output' {function}
271 * @param callbacks.'clear_output' {function}
269 * @param callbacks.'clear_output' {function}
272 * @param callbacks.'set_next_input' {function}
270 * @param callbacks.'set_next_input' {function}
273 * @param {object} [options]
271 * @param {object} [options]
274 * @param [options.silent=false] {Boolean}
272 * @param [options.silent=false] {Boolean}
275 * @param [options.user_expressions=empty_dict] {Dict}
273 * @param [options.user_expressions=empty_dict] {Dict}
276 * @param [options.user_variables=empty_list] {List od Strings}
274 * @param [options.user_variables=empty_list] {List od Strings}
277 * @param [options.allow_stdin=false] {Boolean} true|false
275 * @param [options.allow_stdin=false] {Boolean} true|false
278 *
276 *
279 * @example
277 * @example
280 *
278 *
281 * The options object should contain the options for the execute call. Its default
279 * The options object should contain the options for the execute call. Its default
282 * values are:
280 * values are:
283 *
281 *
284 * options = {
282 * options = {
285 * silent : true,
283 * silent : true,
286 * user_variables : [],
284 * user_variables : [],
287 * user_expressions : {},
285 * user_expressions : {},
288 * allow_stdin : false
286 * allow_stdin : false
289 * }
287 * }
290 *
288 *
291 * When calling this method pass a callbacks structure of the form:
289 * When calling this method pass a callbacks structure of the form:
292 *
290 *
293 * callbacks = {
291 * callbacks = {
294 * 'execute_reply': execute_reply_callback,
292 * 'execute_reply': execute_reply_callback,
295 * 'output': output_callback,
293 * 'output': output_callback,
296 * 'clear_output': clear_output_callback,
294 * 'clear_output': clear_output_callback,
297 * 'set_next_input': set_next_input_callback
295 * 'set_next_input': set_next_input_callback
298 * }
296 * }
299 *
297 *
300 * The `execute_reply_callback` will be passed the content and metadata
298 * The `execute_reply_callback` will be passed the content and metadata
301 * objects of the `execute_reply` message documented
299 * objects of the `execute_reply` message documented
302 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#execute)
300 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#execute)
303 *
301 *
304 * The `output_callback` will be passed `msg_type` ('stream','display_data','pyout','pyerr')
302 * The `output_callback` will be passed `msg_type` ('stream','display_data','pyout','pyerr')
305 * of the output and the content and metadata objects of the PUB/SUB channel that contains the
303 * of the output and the content and metadata objects of the PUB/SUB channel that contains the
306 * output:
304 * output:
307 *
305 *
308 * http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
306 * http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
309 *
307 *
310 * The `clear_output_callback` will be passed a content object that contains
308 * The `clear_output_callback` will be passed a content object that contains
311 * stdout, stderr and other fields that are booleans, as well as the metadata object.
309 * stdout, stderr and other fields that are booleans, as well as the metadata object.
312 *
310 *
313 * The `set_next_input_callback` will be passed the text that should become the next
311 * The `set_next_input_callback` will be passed the text that should become the next
314 * input cell.
312 * input cell.
315 */
313 */
316 Kernel.prototype.execute = function (code, callbacks, options) {
314 Kernel.prototype.execute = function (code, callbacks, options) {
317
315
318 var content = {
316 var content = {
319 code : code,
317 code : code,
320 silent : true,
318 silent : true,
321 store_history : false,
319 store_history : false,
322 user_variables : [],
320 user_variables : [],
323 user_expressions : {},
321 user_expressions : {},
324 allow_stdin : false
322 allow_stdin : false
325 };
323 };
326 callbacks = callbacks || {};
324 callbacks = callbacks || {};
327 if (callbacks.input_request !== undefined) {
325 if (callbacks.input_request !== undefined) {
328 content.allow_stdin = true;
326 content.allow_stdin = true;
329 }
327 }
330 $.extend(true, content, options)
328 $.extend(true, content, options)
331 $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content});
329 $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content});
332 var msg = this._get_msg("execute_request", content);
330 var msg = this._get_msg("execute_request", content);
333 this.shell_channel.send(JSON.stringify(msg));
331 this.shell_channel.send(JSON.stringify(msg));
334 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
332 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
335 return msg.header.msg_id;
333 return msg.header.msg_id;
336 };
334 };
337
335
338 /**
336 /**
339 * When calling this method pass a callbacks structure of the form:
337 * When calling this method pass a callbacks structure of the form:
340 *
338 *
341 * callbacks = {
339 * callbacks = {
342 * 'complete_reply': complete_reply_callback
340 * 'complete_reply': complete_reply_callback
343 * }
341 * }
344 *
342 *
345 * The `complete_reply_callback` will be passed the content object of the
343 * The `complete_reply_callback` will be passed the content object of the
346 * `complete_reply` message documented
344 * `complete_reply` message documented
347 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
345 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
348 *
346 *
349 * @method complete
347 * @method complete
350 * @param line {integer}
348 * @param line {integer}
351 * @param cursor_pos {integer}
349 * @param cursor_pos {integer}
352 * @param {dict} callbacks
350 * @param {dict} callbacks
353 * @param callbacks.complete_reply {function} `complete_reply_callback`
351 * @param callbacks.complete_reply {function} `complete_reply_callback`
354 *
352 *
355 */
353 */
356 Kernel.prototype.complete = function (line, cursor_pos, callbacks) {
354 Kernel.prototype.complete = function (line, cursor_pos, callbacks) {
357 callbacks = callbacks || {};
355 callbacks = callbacks || {};
358 var content = {
356 var content = {
359 text : '',
357 text : '',
360 line : line,
358 line : line,
361 block : null,
359 block : null,
362 cursor_pos : cursor_pos
360 cursor_pos : cursor_pos
363 };
361 };
364 var msg = this._get_msg("complete_request", content);
362 var msg = this._get_msg("complete_request", content);
365 this.shell_channel.send(JSON.stringify(msg));
363 this.shell_channel.send(JSON.stringify(msg));
366 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
364 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
367 return msg.header.msg_id;
365 return msg.header.msg_id;
368 };
366 };
369
367
370
368
371 Kernel.prototype.interrupt = function () {
369 Kernel.prototype.interrupt = function () {
372 if (this.running) {
370 if (this.running) {
373 $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this});
371 $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this});
374 $.post(this.kernel_url + "/interrupt");
372 $.post(this.kernel_url + "/interrupt");
375 };
373 };
376 };
374 };
377
375
378
376
379 Kernel.prototype.kill = function () {
377 Kernel.prototype.kill = function () {
380 if (this.running) {
378 if (this.running) {
381 this.running = false;
379 this.running = false;
382 var settings = {
380 var settings = {
383 cache : false,
381 cache : false,
384 type : "DELETE"
382 type : "DELETE"
385 };
383 };
386 $.ajax(this.kernel_url, settings);
384 $.ajax(this.kernel_url, settings);
387 };
385 };
388 };
386 };
389
387
390 Kernel.prototype.send_input_reply = function (input) {
388 Kernel.prototype.send_input_reply = function (input) {
391 var content = {
389 var content = {
392 value : input,
390 value : input,
393 };
391 };
394 $([IPython.events]).trigger('input_reply.Kernel', {kernel: this, content:content});
392 $([IPython.events]).trigger('input_reply.Kernel', {kernel: this, content:content});
395 var msg = this._get_msg("input_reply", content);
393 var msg = this._get_msg("input_reply", content);
396 this.stdin_channel.send(JSON.stringify(msg));
394 this.stdin_channel.send(JSON.stringify(msg));
397 return msg.header.msg_id;
395 return msg.header.msg_id;
398 };
396 };
399
397
400
398
401 // Reply handlers
399 // Reply handlers
402
400
403 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
401 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
404 var callbacks = this._msg_callbacks[msg_id];
402 var callbacks = this._msg_callbacks[msg_id];
405 return callbacks;
403 return callbacks;
406 };
404 };
407
405
408
406
409 Kernel.prototype.clear_callbacks_for_msg = function (msg_id) {
407 Kernel.prototype.clear_callbacks_for_msg = function (msg_id) {
410 if (this._msg_callbacks[msg_id] !== undefined ) {
408 if (this._msg_callbacks[msg_id] !== undefined ) {
411 delete this._msg_callbacks[msg_id];
409 delete this._msg_callbacks[msg_id];
412 }
410 }
413 };
411 };
414
412
415
413
416 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
414 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
417 this._msg_callbacks[msg_id] = callbacks || {};
415 this._msg_callbacks[msg_id] = callbacks || {};
418 };
416 };
419
417
420
418
421 Kernel.prototype._handle_shell_reply = function (e) {
419 Kernel.prototype._handle_shell_reply = function (e) {
422 var reply = $.parseJSON(e.data);
420 var reply = $.parseJSON(e.data);
423 $([IPython.events]).trigger('shell_reply.Kernel', {kernel: this, reply:reply});
421 $([IPython.events]).trigger('shell_reply.Kernel', {kernel: this, reply:reply});
424 var header = reply.header;
422 var header = reply.header;
425 var content = reply.content;
423 var content = reply.content;
426 var metadata = reply.metadata;
424 var metadata = reply.metadata;
427 var msg_type = header.msg_type;
425 var msg_type = header.msg_type;
428 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
426 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
429 if (callbacks !== undefined) {
427 if (callbacks !== undefined) {
430 var cb = callbacks[msg_type];
428 var cb = callbacks[msg_type];
431 if (cb !== undefined) {
429 if (cb !== undefined) {
432 cb(content, metadata);
430 cb(content, metadata);
433 }
431 }
434 };
432 };
435
433
436 if (content.payload !== undefined) {
434 if (content.payload !== undefined) {
437 var payload = content.payload || [];
435 var payload = content.payload || [];
438 this._handle_payload(callbacks, payload);
436 this._handle_payload(callbacks, payload);
439 }
437 }
440 };
438 };
441
439
442
440
443 Kernel.prototype._handle_payload = function (callbacks, payload) {
441 Kernel.prototype._handle_payload = function (callbacks, payload) {
444 var l = payload.length;
442 var l = payload.length;
445 // Payloads are handled by triggering events because we don't want the Kernel
443 // Payloads are handled by triggering events because we don't want the Kernel
446 // to depend on the Notebook or Pager classes.
444 // to depend on the Notebook or Pager classes.
447 for (var i=0; i<l; i++) {
445 for (var i=0; i<l; i++) {
448 if (payload[i].source === 'page') {
446 if (payload[i].source === 'page') {
449 var data = {'text':payload[i].text}
447 var data = {'text':payload[i].text}
450 $([IPython.events]).trigger('open_with_text.Pager', data);
448 $([IPython.events]).trigger('open_with_text.Pager', data);
451 } else if (payload[i].source === 'set_next_input') {
449 } else if (payload[i].source === 'set_next_input') {
452 if (callbacks.set_next_input !== undefined) {
450 if (callbacks.set_next_input !== undefined) {
453 callbacks.set_next_input(payload[i].text)
451 callbacks.set_next_input(payload[i].text)
454 }
452 }
455 }
453 }
456 };
454 };
457 };
455 };
458
456
459
457
460 Kernel.prototype._handle_iopub_reply = function (e) {
458 Kernel.prototype._handle_iopub_reply = function (e) {
461 var reply = $.parseJSON(e.data);
459 var reply = $.parseJSON(e.data);
462 var content = reply.content;
460 var content = reply.content;
463 var msg_type = reply.header.msg_type;
461 var msg_type = reply.header.msg_type;
464 var metadata = reply.metadata;
462 var metadata = reply.metadata;
465 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
463 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
466 if (msg_type !== 'status' && callbacks === undefined) {
464 if (msg_type !== 'status' && callbacks === undefined) {
467 // Message not from one of this notebook's cells and there are no
465 // Message not from one of this notebook's cells and there are no
468 // callbacks to handle it.
466 // callbacks to handle it.
469 return;
467 return;
470 }
468 }
471 var output_types = ['stream','display_data','pyout','pyerr'];
469 var output_types = ['stream','display_data','pyout','pyerr'];
472 if (output_types.indexOf(msg_type) >= 0) {
470 if (output_types.indexOf(msg_type) >= 0) {
473 var cb = callbacks['output'];
471 var cb = callbacks['output'];
474 if (cb !== undefined) {
472 if (cb !== undefined) {
475 cb(msg_type, content, metadata);
473 cb(msg_type, content, metadata);
476 }
474 }
477 } else if (msg_type === 'status') {
475 } else if (msg_type === 'status') {
478 if (content.execution_state === 'busy') {
476 if (content.execution_state === 'busy') {
479 $([IPython.events]).trigger('status_busy.Kernel', {kernel: this});
477 $([IPython.events]).trigger('status_busy.Kernel', {kernel: this});
480 } else if (content.execution_state === 'idle') {
478 } else if (content.execution_state === 'idle') {
481 $([IPython.events]).trigger('status_idle.Kernel', {kernel: this});
479 $([IPython.events]).trigger('status_idle.Kernel', {kernel: this});
482 } else if (content.execution_state === 'restarting') {
480 } else if (content.execution_state === 'restarting') {
483 // autorestarting is distinct from restarting,
481 // autorestarting is distinct from restarting,
484 // in that it means the kernel died and the server is restarting it.
482 // in that it means the kernel died and the server is restarting it.
485 // status_restarting sets the notification widget,
483 // status_restarting sets the notification widget,
486 // autorestart shows the more prominent dialog.
484 // autorestart shows the more prominent dialog.
487 $([IPython.events]).trigger('status_autorestarting.Kernel', {kernel: this});
485 $([IPython.events]).trigger('status_autorestarting.Kernel', {kernel: this});
488 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
486 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
489 } else if (content.execution_state === 'dead') {
487 } else if (content.execution_state === 'dead') {
490 this.stop_channels();
488 this.stop_channels();
491 $([IPython.events]).trigger('status_dead.Kernel', {kernel: this});
489 $([IPython.events]).trigger('status_dead.Kernel', {kernel: this});
492 };
490 };
493 } else if (msg_type === 'clear_output') {
491 } else if (msg_type === 'clear_output') {
494 var cb = callbacks['clear_output'];
492 var cb = callbacks['clear_output'];
495 if (cb !== undefined) {
493 if (cb !== undefined) {
496 cb(content, metadata);
494 cb(content, metadata);
497 }
495 }
498 };
496 };
499 };
497 };
500
498
501
499
502 Kernel.prototype._handle_input_request = function (e) {
500 Kernel.prototype._handle_input_request = function (e) {
503 var request = $.parseJSON(e.data);
501 var request = $.parseJSON(e.data);
504 var header = request.header;
502 var header = request.header;
505 var content = request.content;
503 var content = request.content;
506 var metadata = request.metadata;
504 var metadata = request.metadata;
507 var msg_type = header.msg_type;
505 var msg_type = header.msg_type;
508 if (msg_type !== 'input_request') {
506 if (msg_type !== 'input_request') {
509 console.log("Invalid input request!", request);
507 console.log("Invalid input request!", request);
510 return;
508 return;
511 }
509 }
512 var callbacks = this.get_callbacks_for_msg(request.parent_header.msg_id);
510 var callbacks = this.get_callbacks_for_msg(request.parent_header.msg_id);
513 if (callbacks !== undefined) {
511 if (callbacks !== undefined) {
514 var cb = callbacks[msg_type];
512 var cb = callbacks[msg_type];
515 if (cb !== undefined) {
513 if (cb !== undefined) {
516 cb(content, metadata);
514 cb(content, metadata);
517 }
515 }
518 };
516 };
519 };
517 };
520
518
521
519
522 IPython.Kernel = Kernel;
520 IPython.Kernel = Kernel;
523
521
524 return IPython;
522 return IPython;
525
523
526 }(IPython));
524 }(IPython));
527
525
@@ -1,96 +1,95 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Notebook
9 // Notebook
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var Session = function(notebook_path, Notebook){
14 var Session = function(notebook_path, Notebook){
15 this.kernel = null;
15 this.kernel = null;
16 this.kernel_id = null;
16 this.kernel_id = null;
17 this.session_id = null;
17 this.session_id = null;
18 this.notebook_path = notebook_path;
18 this.notebook_path = notebook_path;
19 this.notebook = Notebook;
19 this.notebook = Notebook;
20 this._baseProjectUrl = Notebook.baseProjectUrl()
20 this._baseProjectUrl = Notebook.baseProjectUrl()
21 };
21 };
22
22
23 Session.prototype.start = function(){
23 Session.prototype.start = function(){
24 var that = this
24 var that = this
25 var qs = $.param({notebook_path:this.notebook_path});
25 var qs = $.param({notebook_path:this.notebook_path});
26 var url = '/api/sessions' + '?' + qs;
26 var url = '/api/sessions' + '?' + qs;
27 $.post(url,
27 $.post(url,
28 $.proxy(this.start_kernel, that),
28 $.proxy(this.start_kernel, that),
29 'json'
29 'json'
30 );
30 );
31 };
31 };
32
32
33 Session.prototype.notebook_rename = function (notebook_path) {
33 Session.prototype.notebook_rename = function (notebook_path) {
34 this.notebook_path = notebook_path;
34 this.notebook_path = notebook_path;
35 var settings = {
35 var settings = {
36 processData : false,
36 processData : false,
37 cache : false,
37 cache : false,
38 type : "PATCH",
38 type : "PATCH",
39 data: notebook_path,
39 data: notebook_path,
40 dataType : "json",
40 dataType : "json",
41 };
41 };
42 var url = this._baseProjectUrl + 'api/sessions/' + this.session_id;
42 var url = this._baseProjectUrl + 'api/sessions/' + this.session_id;
43 $.ajax(url, settings);
43 $.ajax(url, settings);
44 }
44 }
45
45
46
46
47 Session.prototype.delete_session = function() {
47 Session.prototype.delete_session = function() {
48 var settings = {
48 var settings = {
49 processData : false,
49 processData : false,
50 cache : false,
50 cache : false,
51 type : "DELETE",
51 type : "DELETE",
52 dataType : "json",
52 dataType : "json",
53 };
53 };
54 var url = this._baseProjectUrl + 'api/sessions/' + this.session_id;
54 var url = this._baseProjectUrl + 'api/sessions/' + this.session_id;
55 $.ajax(url, settings);
55 $.ajax(url, settings);
56 };
56 };
57
57
58 // Kernel related things
58 // Kernel related things
59 /**
59 /**
60 * Start a new kernel and set it on each code cell.
60 * Start a new kernel and set it on each code cell.
61 *
61 *
62 * @method start_kernel
62 * @method start_kernel
63 */
63 */
64 Session.prototype.start_kernel = function (json) {
64 Session.prototype.start_kernel = function (json) {
65 this.session_id = json.id;
65 this.session_id = json.id;
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
73 /**
72 /**
74 * Prompt the user to restart the IPython kernel.
73 * Prompt the user to restart the IPython kernel.
75 *
74 *
76 * @method restart_kernel
75 * @method restart_kernel
77 */
76 */
78 Session.prototype.restart_kernel = function () {
77 Session.prototype.restart_kernel = function () {
79 this.kernel.restart();
78 this.kernel.restart();
80 };
79 };
81
80
82 Session.prototype.interrupt_kernel = function() {
81 Session.prototype.interrupt_kernel = function() {
83 this.kernel.interrupt();
82 this.kernel.interrupt();
84 };
83 };
85
84
86
85
87 Session.prototype.kill_kernel = function() {
86 Session.prototype.kill_kernel = function() {
88 this.kernel.kill();
87 this.kernel.kill();
89 };
88 };
90
89
91 IPython.Session = Session;
90 IPython.Session = Session;
92
91
93
92
94 return IPython;
93 return IPython;
95
94
96 }(IPython));
95 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now