##// END OF EJS Templates
move check_for_zmq to utils.zmqrelated
MinRK -
Show More
@@ -0,0 +1,47 b''
1 """utilities for checking zmq versions"""
2 #-----------------------------------------------------------------------------
3 # Copyright (C) 2013 The IPython Development Team
4 #
5 # Distributed under the terms of the BSD License. The full license is in
6 # the file COPYING.txt, distributed as part of this software.
7 #-----------------------------------------------------------------------------
8
9 #-----------------------------------------------------------------------------
10 # Verify zmq version dependency >= 2.1.11
11 #-----------------------------------------------------------------------------
12
13 import warnings
14 from IPython.utils.version import check_version
15
16
17 def patch_pyzmq():
18 """backport a few patches from newer pyzmq
19
20 These can be removed as we bump our minimum pyzmq version
21 """
22
23 import zmq
24
25 # fallback on stdlib json if jsonlib is selected, because jsonlib breaks things.
26 # jsonlib support is removed from pyzmq >= 2.2.0
27
28 from zmq.utils import jsonapi
29 if jsonapi.jsonmod.__name__ == 'jsonlib':
30 import json
31 jsonapi.jsonmod = json
32
33
34 def check_for_zmq(minimum_version, required_by='Someone'):
35 try:
36 import zmq
37 except ImportError:
38 raise ImportError("%s requires pyzmq >= %s"%(required_by, minimum_version))
39
40 patch_pyzmq()
41
42 pyzmq_version = zmq.__version__
43
44 if not check_version(pyzmq_version, minimum_version):
45 raise ImportError("%s requires pyzmq >= %s, but you have %s"%(
46 required_by, minimum_version, pyzmq_version))
47
@@ -1,718 +1,718 b''
1 # coding: utf-8
1 # coding: utf-8
2 """A tornado based IPython notebook server.
2 """A tornado based IPython notebook server.
3
3
4 Authors:
4 Authors:
5
5
6 * Brian Granger
6 * Brian Granger
7 """
7 """
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2011 The IPython Development Team
9 # Copyright (C) 2008-2011 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 # stdlib
19 # stdlib
20 import errno
20 import errno
21 import logging
21 import logging
22 import os
22 import os
23 import random
23 import random
24 import re
24 import re
25 import select
25 import select
26 import signal
26 import signal
27 import socket
27 import socket
28 import sys
28 import sys
29 import threading
29 import threading
30 import time
30 import time
31 import uuid
31 import uuid
32 import webbrowser
32 import webbrowser
33
33
34
34
35 # Third party
35 # Third party
36 # check for pyzmq 2.1.11 (this is actually redundant)
36 # check for pyzmq 2.1.11
37 from IPython.kernel.zmq import check_for_zmq
37 from IPython.utils.zmqrelated import check_for_zmq
38 check_for_zmq('2.1.11', 'IPython.frontend.html.notebook')
38 check_for_zmq('2.1.11', 'IPython.frontend.html.notebook')
39
39
40 import zmq
40 import zmq
41 from jinja2 import Environment, FileSystemLoader
41 from jinja2 import Environment, FileSystemLoader
42
42
43 # Install the pyzmq ioloop. This has to be done before anything else from
43 # Install the pyzmq ioloop. This has to be done before anything else from
44 # tornado is imported.
44 # tornado is imported.
45 from zmq.eventloop import ioloop
45 from zmq.eventloop import ioloop
46 ioloop.install()
46 ioloop.install()
47
47
48 # check for tornado 2.1.0
48 # check for tornado 2.1.0
49 msg = "The IPython Notebook requires tornado >= 2.1.0"
49 msg = "The IPython Notebook requires tornado >= 2.1.0"
50 try:
50 try:
51 import tornado
51 import tornado
52 except ImportError:
52 except ImportError:
53 raise ImportError(msg)
53 raise ImportError(msg)
54 try:
54 try:
55 version_info = tornado.version_info
55 version_info = tornado.version_info
56 except AttributeError:
56 except AttributeError:
57 raise ImportError(msg + ", but you have < 1.1.0")
57 raise ImportError(msg + ", but you have < 1.1.0")
58 if version_info < (2,1,0):
58 if version_info < (2,1,0):
59 raise ImportError(msg + ", but you have %s" % tornado.version)
59 raise ImportError(msg + ", but you have %s" % tornado.version)
60
60
61 from tornado import httpserver
61 from tornado import httpserver
62 from tornado import web
62 from tornado import web
63
63
64 # Our own libraries
64 # Our own libraries
65 from IPython.frontend.html.notebook import DEFAULT_STATIC_FILES_PATH
65 from IPython.frontend.html.notebook import DEFAULT_STATIC_FILES_PATH
66 from .kernelmanager import MappingKernelManager
66 from .kernelmanager import MappingKernelManager
67 from .handlers import (LoginHandler, LogoutHandler,
67 from .handlers import (LoginHandler, LogoutHandler,
68 ProjectDashboardHandler, NewHandler, NamedNotebookHandler,
68 ProjectDashboardHandler, NewHandler, NamedNotebookHandler,
69 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
69 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
70 ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler,
70 ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler,
71 RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler,
71 RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler,
72 MainClusterHandler, ClusterProfileHandler, ClusterActionHandler,
72 MainClusterHandler, ClusterProfileHandler, ClusterActionHandler,
73 FileFindHandler, NotebookRedirectHandler,
73 FileFindHandler, NotebookRedirectHandler,
74 )
74 )
75 from .nbmanager import NotebookManager
75 from .nbmanager import NotebookManager
76 from .filenbmanager import FileNotebookManager
76 from .filenbmanager import FileNotebookManager
77 from .clustermanager import ClusterManager
77 from .clustermanager import ClusterManager
78
78
79 from IPython.config.application import catch_config_error, boolean_flag
79 from IPython.config.application import catch_config_error, boolean_flag
80 from IPython.core.application import BaseIPythonApplication
80 from IPython.core.application import BaseIPythonApplication
81 from IPython.core.profiledir import ProfileDir
81 from IPython.core.profiledir import ProfileDir
82 from IPython.frontend.consoleapp import IPythonConsoleApp
82 from IPython.frontend.consoleapp import IPythonConsoleApp
83 from IPython.kernel import swallow_argv
83 from IPython.kernel import swallow_argv
84 from IPython.kernel.zmq.session import Session, default_secure
84 from IPython.kernel.zmq.session import Session, default_secure
85 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
85 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
86 from IPython.kernel.zmq.kernelapp import (
86 from IPython.kernel.zmq.kernelapp import (
87 kernel_flags,
87 kernel_flags,
88 kernel_aliases,
88 kernel_aliases,
89 IPKernelApp
89 IPKernelApp
90 )
90 )
91 from IPython.utils.importstring import import_item
91 from IPython.utils.importstring import import_item
92 from IPython.utils.localinterfaces import LOCALHOST
92 from IPython.utils.localinterfaces import LOCALHOST
93 from IPython.utils.traitlets import (
93 from IPython.utils.traitlets import (
94 Dict, Unicode, Integer, List, Enum, Bool,
94 Dict, Unicode, Integer, List, Enum, Bool,
95 DottedObjectName
95 DottedObjectName
96 )
96 )
97 from IPython.utils import py3compat
97 from IPython.utils import py3compat
98 from IPython.utils.path import filefind
98 from IPython.utils.path import filefind
99
99
100 #-----------------------------------------------------------------------------
100 #-----------------------------------------------------------------------------
101 # Module globals
101 # Module globals
102 #-----------------------------------------------------------------------------
102 #-----------------------------------------------------------------------------
103
103
104 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
104 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
105 _kernel_action_regex = r"(?P<action>restart|interrupt)"
105 _kernel_action_regex = r"(?P<action>restart|interrupt)"
106 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
106 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
107 _notebook_name_regex = r"(?P<notebook_name>.+\.ipynb)"
107 _notebook_name_regex = r"(?P<notebook_name>.+\.ipynb)"
108 _profile_regex = r"(?P<profile>[^\/]+)" # there is almost no text that is invalid
108 _profile_regex = r"(?P<profile>[^\/]+)" # there is almost no text that is invalid
109 _cluster_action_regex = r"(?P<action>start|stop)"
109 _cluster_action_regex = r"(?P<action>start|stop)"
110
110
111 _examples = """
111 _examples = """
112 ipython notebook # start the notebook
112 ipython notebook # start the notebook
113 ipython notebook --profile=sympy # use the sympy profile
113 ipython notebook --profile=sympy # use the sympy profile
114 ipython notebook --pylab=inline # pylab in inline plotting mode
114 ipython notebook --pylab=inline # pylab in inline plotting mode
115 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
115 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
116 ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces
116 ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces
117 """
117 """
118
118
119 #-----------------------------------------------------------------------------
119 #-----------------------------------------------------------------------------
120 # Helper functions
120 # Helper functions
121 #-----------------------------------------------------------------------------
121 #-----------------------------------------------------------------------------
122
122
123 def url_path_join(a,b):
123 def url_path_join(a,b):
124 if a.endswith('/') and b.startswith('/'):
124 if a.endswith('/') and b.startswith('/'):
125 return a[:-1]+b
125 return a[:-1]+b
126 else:
126 else:
127 return a+b
127 return a+b
128
128
129 def random_ports(port, n):
129 def random_ports(port, n):
130 """Generate a list of n random ports near the given port.
130 """Generate a list of n random ports near the given port.
131
131
132 The first 5 ports will be sequential, and the remaining n-5 will be
132 The first 5 ports will be sequential, and the remaining n-5 will be
133 randomly selected in the range [port-2*n, port+2*n].
133 randomly selected in the range [port-2*n, port+2*n].
134 """
134 """
135 for i in range(min(5, n)):
135 for i in range(min(5, n)):
136 yield port + i
136 yield port + i
137 for i in range(n-5):
137 for i in range(n-5):
138 yield port + random.randint(-2*n, 2*n)
138 yield port + random.randint(-2*n, 2*n)
139
139
140 #-----------------------------------------------------------------------------
140 #-----------------------------------------------------------------------------
141 # The Tornado web application
141 # The Tornado web application
142 #-----------------------------------------------------------------------------
142 #-----------------------------------------------------------------------------
143
143
144 class NotebookWebApplication(web.Application):
144 class NotebookWebApplication(web.Application):
145
145
146 def __init__(self, ipython_app, kernel_manager, notebook_manager,
146 def __init__(self, ipython_app, kernel_manager, notebook_manager,
147 cluster_manager, log,
147 cluster_manager, log,
148 base_project_url, settings_overrides):
148 base_project_url, settings_overrides):
149 handlers = [
149 handlers = [
150 (r"/", ProjectDashboardHandler),
150 (r"/", ProjectDashboardHandler),
151 (r"/login", LoginHandler),
151 (r"/login", LoginHandler),
152 (r"/logout", LogoutHandler),
152 (r"/logout", LogoutHandler),
153 (r"/new", NewHandler),
153 (r"/new", NewHandler),
154 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
154 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
155 (r"/%s" % _notebook_name_regex, NotebookRedirectHandler),
155 (r"/%s" % _notebook_name_regex, NotebookRedirectHandler),
156 (r"/%s/copy" % _notebook_id_regex, NotebookCopyHandler),
156 (r"/%s/copy" % _notebook_id_regex, NotebookCopyHandler),
157 (r"/%s/print" % _notebook_id_regex, PrintNotebookHandler),
157 (r"/%s/print" % _notebook_id_regex, PrintNotebookHandler),
158 (r"/kernels", MainKernelHandler),
158 (r"/kernels", MainKernelHandler),
159 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
159 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
160 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
160 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
161 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
161 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
162 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
162 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
163 (r"/notebooks", NotebookRootHandler),
163 (r"/notebooks", NotebookRootHandler),
164 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
164 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
165 (r"/rstservice/render", RSTHandler),
165 (r"/rstservice/render", RSTHandler),
166 (r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}),
166 (r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}),
167 (r"/clusters", MainClusterHandler),
167 (r"/clusters", MainClusterHandler),
168 (r"/clusters/%s/%s" % (_profile_regex, _cluster_action_regex), ClusterActionHandler),
168 (r"/clusters/%s/%s" % (_profile_regex, _cluster_action_regex), ClusterActionHandler),
169 (r"/clusters/%s" % _profile_regex, ClusterProfileHandler),
169 (r"/clusters/%s" % _profile_regex, ClusterProfileHandler),
170 ]
170 ]
171
171
172 # Python < 2.6.5 doesn't accept unicode keys in f(**kwargs), and
172 # Python < 2.6.5 doesn't accept unicode keys in f(**kwargs), and
173 # base_project_url will always be unicode, which will in turn
173 # base_project_url will always be unicode, which will in turn
174 # make the patterns unicode, and ultimately result in unicode
174 # make the patterns unicode, and ultimately result in unicode
175 # keys in kwargs to handler._execute(**kwargs) in tornado.
175 # keys in kwargs to handler._execute(**kwargs) in tornado.
176 # This enforces that base_project_url be ascii in that situation.
176 # This enforces that base_project_url be ascii in that situation.
177 #
177 #
178 # Note that the URLs these patterns check against are escaped,
178 # Note that the URLs these patterns check against are escaped,
179 # and thus guaranteed to be ASCII: 'hΓ©llo' is really 'h%C3%A9llo'.
179 # and thus guaranteed to be ASCII: 'hΓ©llo' is really 'h%C3%A9llo'.
180 base_project_url = py3compat.unicode_to_str(base_project_url, 'ascii')
180 base_project_url = py3compat.unicode_to_str(base_project_url, 'ascii')
181
181
182 settings = dict(
182 settings = dict(
183 template_path=os.path.join(os.path.dirname(__file__), "templates"),
183 template_path=os.path.join(os.path.dirname(__file__), "templates"),
184 static_path=ipython_app.static_file_path,
184 static_path=ipython_app.static_file_path,
185 static_handler_class = FileFindHandler,
185 static_handler_class = FileFindHandler,
186 static_url_prefix = url_path_join(base_project_url,'/static/'),
186 static_url_prefix = url_path_join(base_project_url,'/static/'),
187 cookie_secret=os.urandom(1024),
187 cookie_secret=os.urandom(1024),
188 login_url=url_path_join(base_project_url,'/login'),
188 login_url=url_path_join(base_project_url,'/login'),
189 cookie_name='username-%s' % uuid.uuid4(),
189 cookie_name='username-%s' % uuid.uuid4(),
190 base_project_url = base_project_url,
190 base_project_url = base_project_url,
191 )
191 )
192
192
193 # allow custom overrides for the tornado web app.
193 # allow custom overrides for the tornado web app.
194 settings.update(settings_overrides)
194 settings.update(settings_overrides)
195
195
196 # prepend base_project_url onto the patterns that we match
196 # prepend base_project_url onto the patterns that we match
197 new_handlers = []
197 new_handlers = []
198 for handler in handlers:
198 for handler in handlers:
199 pattern = url_path_join(base_project_url, handler[0])
199 pattern = url_path_join(base_project_url, handler[0])
200 new_handler = tuple([pattern]+list(handler[1:]))
200 new_handler = tuple([pattern]+list(handler[1:]))
201 new_handlers.append( new_handler )
201 new_handlers.append( new_handler )
202
202
203 super(NotebookWebApplication, self).__init__(new_handlers, **settings)
203 super(NotebookWebApplication, self).__init__(new_handlers, **settings)
204
204
205 self.kernel_manager = kernel_manager
205 self.kernel_manager = kernel_manager
206 self.notebook_manager = notebook_manager
206 self.notebook_manager = notebook_manager
207 self.cluster_manager = cluster_manager
207 self.cluster_manager = cluster_manager
208 self.ipython_app = ipython_app
208 self.ipython_app = ipython_app
209 self.read_only = self.ipython_app.read_only
209 self.read_only = self.ipython_app.read_only
210 self.config = self.ipython_app.config
210 self.config = self.ipython_app.config
211 self.use_less = self.ipython_app.use_less
211 self.use_less = self.ipython_app.use_less
212 self.log = log
212 self.log = log
213 self.jinja2_env = Environment(loader=FileSystemLoader(os.path.join(os.path.dirname(__file__), "templates")))
213 self.jinja2_env = Environment(loader=FileSystemLoader(os.path.join(os.path.dirname(__file__), "templates")))
214
214
215
215
216
216
217 #-----------------------------------------------------------------------------
217 #-----------------------------------------------------------------------------
218 # Aliases and Flags
218 # Aliases and Flags
219 #-----------------------------------------------------------------------------
219 #-----------------------------------------------------------------------------
220
220
221 flags = dict(kernel_flags)
221 flags = dict(kernel_flags)
222 flags['no-browser']=(
222 flags['no-browser']=(
223 {'NotebookApp' : {'open_browser' : False}},
223 {'NotebookApp' : {'open_browser' : False}},
224 "Don't open the notebook in a browser after startup."
224 "Don't open the notebook in a browser after startup."
225 )
225 )
226 flags['no-mathjax']=(
226 flags['no-mathjax']=(
227 {'NotebookApp' : {'enable_mathjax' : False}},
227 {'NotebookApp' : {'enable_mathjax' : False}},
228 """Disable MathJax
228 """Disable MathJax
229
229
230 MathJax is the javascript library IPython uses to render math/LaTeX. It is
230 MathJax is the javascript library IPython uses to render math/LaTeX. It is
231 very large, so you may want to disable it if you have a slow internet
231 very large, so you may want to disable it if you have a slow internet
232 connection, or for offline use of the notebook.
232 connection, or for offline use of the notebook.
233
233
234 When disabled, equations etc. will appear as their untransformed TeX source.
234 When disabled, equations etc. will appear as their untransformed TeX source.
235 """
235 """
236 )
236 )
237 flags['read-only'] = (
237 flags['read-only'] = (
238 {'NotebookApp' : {'read_only' : True}},
238 {'NotebookApp' : {'read_only' : True}},
239 """Allow read-only access to notebooks.
239 """Allow read-only access to notebooks.
240
240
241 When using a password to protect the notebook server, this flag
241 When using a password to protect the notebook server, this flag
242 allows unauthenticated clients to view the notebook list, and
242 allows unauthenticated clients to view the notebook list, and
243 individual notebooks, but not edit them, start kernels, or run
243 individual notebooks, but not edit them, start kernels, or run
244 code.
244 code.
245
245
246 If no password is set, the server will be entirely read-only.
246 If no password is set, the server will be entirely read-only.
247 """
247 """
248 )
248 )
249
249
250 # Add notebook manager flags
250 # Add notebook manager flags
251 flags.update(boolean_flag('script', 'FileNotebookManager.save_script',
251 flags.update(boolean_flag('script', 'FileNotebookManager.save_script',
252 'Auto-save a .py script everytime the .ipynb notebook is saved',
252 'Auto-save a .py script everytime the .ipynb notebook is saved',
253 'Do not auto-save .py scripts for every notebook'))
253 'Do not auto-save .py scripts for every notebook'))
254
254
255 # the flags that are specific to the frontend
255 # the flags that are specific to the frontend
256 # these must be scrubbed before being passed to the kernel,
256 # these must be scrubbed before being passed to the kernel,
257 # or it will raise an error on unrecognized flags
257 # or it will raise an error on unrecognized flags
258 notebook_flags = ['no-browser', 'no-mathjax', 'read-only', 'script', 'no-script']
258 notebook_flags = ['no-browser', 'no-mathjax', 'read-only', 'script', 'no-script']
259
259
260 aliases = dict(kernel_aliases)
260 aliases = dict(kernel_aliases)
261
261
262 aliases.update({
262 aliases.update({
263 'ip': 'NotebookApp.ip',
263 'ip': 'NotebookApp.ip',
264 'port': 'NotebookApp.port',
264 'port': 'NotebookApp.port',
265 'port-retries': 'NotebookApp.port_retries',
265 'port-retries': 'NotebookApp.port_retries',
266 'transport': 'KernelManager.transport',
266 'transport': 'KernelManager.transport',
267 'keyfile': 'NotebookApp.keyfile',
267 'keyfile': 'NotebookApp.keyfile',
268 'certfile': 'NotebookApp.certfile',
268 'certfile': 'NotebookApp.certfile',
269 'notebook-dir': 'NotebookManager.notebook_dir',
269 'notebook-dir': 'NotebookManager.notebook_dir',
270 'browser': 'NotebookApp.browser',
270 'browser': 'NotebookApp.browser',
271 })
271 })
272
272
273 # remove ipkernel flags that are singletons, and don't make sense in
273 # remove ipkernel flags that are singletons, and don't make sense in
274 # multi-kernel evironment:
274 # multi-kernel evironment:
275 aliases.pop('f', None)
275 aliases.pop('f', None)
276
276
277 notebook_aliases = [u'port', u'port-retries', u'ip', u'keyfile', u'certfile',
277 notebook_aliases = [u'port', u'port-retries', u'ip', u'keyfile', u'certfile',
278 u'notebook-dir']
278 u'notebook-dir']
279
279
280 #-----------------------------------------------------------------------------
280 #-----------------------------------------------------------------------------
281 # NotebookApp
281 # NotebookApp
282 #-----------------------------------------------------------------------------
282 #-----------------------------------------------------------------------------
283
283
284 class NotebookApp(BaseIPythonApplication):
284 class NotebookApp(BaseIPythonApplication):
285
285
286 name = 'ipython-notebook'
286 name = 'ipython-notebook'
287 default_config_file_name='ipython_notebook_config.py'
287 default_config_file_name='ipython_notebook_config.py'
288
288
289 description = """
289 description = """
290 The IPython HTML Notebook.
290 The IPython HTML Notebook.
291
291
292 This launches a Tornado based HTML Notebook Server that serves up an
292 This launches a Tornado based HTML Notebook Server that serves up an
293 HTML5/Javascript Notebook client.
293 HTML5/Javascript Notebook client.
294 """
294 """
295 examples = _examples
295 examples = _examples
296
296
297 classes = IPythonConsoleApp.classes + [MappingKernelManager, NotebookManager,
297 classes = IPythonConsoleApp.classes + [MappingKernelManager, NotebookManager,
298 FileNotebookManager]
298 FileNotebookManager]
299 flags = Dict(flags)
299 flags = Dict(flags)
300 aliases = Dict(aliases)
300 aliases = Dict(aliases)
301
301
302 kernel_argv = List(Unicode)
302 kernel_argv = List(Unicode)
303
303
304 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
304 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
305 default_value=logging.INFO,
305 default_value=logging.INFO,
306 config=True,
306 config=True,
307 help="Set the log level by value or name.")
307 help="Set the log level by value or name.")
308
308
309 # create requested profiles by default, if they don't exist:
309 # create requested profiles by default, if they don't exist:
310 auto_create = Bool(True)
310 auto_create = Bool(True)
311
311
312 # file to be opened in the notebook server
312 # file to be opened in the notebook server
313 file_to_run = Unicode('')
313 file_to_run = Unicode('')
314
314
315 # Network related information.
315 # Network related information.
316
316
317 ip = Unicode(LOCALHOST, config=True,
317 ip = Unicode(LOCALHOST, config=True,
318 help="The IP address the notebook server will listen on."
318 help="The IP address the notebook server will listen on."
319 )
319 )
320
320
321 def _ip_changed(self, name, old, new):
321 def _ip_changed(self, name, old, new):
322 if new == u'*': self.ip = u''
322 if new == u'*': self.ip = u''
323
323
324 port = Integer(8888, config=True,
324 port = Integer(8888, config=True,
325 help="The port the notebook server will listen on."
325 help="The port the notebook server will listen on."
326 )
326 )
327 port_retries = Integer(50, config=True,
327 port_retries = Integer(50, config=True,
328 help="The number of additional ports to try if the specified port is not available."
328 help="The number of additional ports to try if the specified port is not available."
329 )
329 )
330
330
331 certfile = Unicode(u'', config=True,
331 certfile = Unicode(u'', config=True,
332 help="""The full path to an SSL/TLS certificate file."""
332 help="""The full path to an SSL/TLS certificate file."""
333 )
333 )
334
334
335 keyfile = Unicode(u'', config=True,
335 keyfile = Unicode(u'', config=True,
336 help="""The full path to a private key file for usage with SSL/TLS."""
336 help="""The full path to a private key file for usage with SSL/TLS."""
337 )
337 )
338
338
339 password = Unicode(u'', config=True,
339 password = Unicode(u'', config=True,
340 help="""Hashed password to use for web authentication.
340 help="""Hashed password to use for web authentication.
341
341
342 To generate, type in a python/IPython shell:
342 To generate, type in a python/IPython shell:
343
343
344 from IPython.lib import passwd; passwd()
344 from IPython.lib import passwd; passwd()
345
345
346 The string should be of the form type:salt:hashed-password.
346 The string should be of the form type:salt:hashed-password.
347 """
347 """
348 )
348 )
349
349
350 open_browser = Bool(True, config=True,
350 open_browser = Bool(True, config=True,
351 help="""Whether to open in a browser after starting.
351 help="""Whether to open in a browser after starting.
352 The specific browser used is platform dependent and
352 The specific browser used is platform dependent and
353 determined by the python standard library `webbrowser`
353 determined by the python standard library `webbrowser`
354 module, unless it is overridden using the --browser
354 module, unless it is overridden using the --browser
355 (NotebookApp.browser) configuration option.
355 (NotebookApp.browser) configuration option.
356 """)
356 """)
357
357
358 browser = Unicode(u'', config=True,
358 browser = Unicode(u'', config=True,
359 help="""Specify what command to use to invoke a web
359 help="""Specify what command to use to invoke a web
360 browser when opening the notebook. If not specified, the
360 browser when opening the notebook. If not specified, the
361 default browser will be determined by the `webbrowser`
361 default browser will be determined by the `webbrowser`
362 standard library module, which allows setting of the
362 standard library module, which allows setting of the
363 BROWSER environment variable to override it.
363 BROWSER environment variable to override it.
364 """)
364 """)
365
365
366 read_only = Bool(False, config=True,
366 read_only = Bool(False, config=True,
367 help="Whether to prevent editing/execution of notebooks."
367 help="Whether to prevent editing/execution of notebooks."
368 )
368 )
369
369
370 use_less = Bool(False, config=True,
370 use_less = Bool(False, config=True,
371 help="""Wether to use Browser Side less-css parsing
371 help="""Wether to use Browser Side less-css parsing
372 instead of compiled css version in templates that allows
372 instead of compiled css version in templates that allows
373 it. This is mainly convenient when working on the less
373 it. This is mainly convenient when working on the less
374 file to avoid a build step, or if user want to overwrite
374 file to avoid a build step, or if user want to overwrite
375 some of the less variables without having to recompile
375 some of the less variables without having to recompile
376 everything.
376 everything.
377
377
378 You will need to install the less.js component in the static directory
378 You will need to install the less.js component in the static directory
379 either in the source tree or in your profile folder.
379 either in the source tree or in your profile folder.
380 """)
380 """)
381
381
382 webapp_settings = Dict(config=True,
382 webapp_settings = Dict(config=True,
383 help="Supply overrides for the tornado.web.Application that the "
383 help="Supply overrides for the tornado.web.Application that the "
384 "IPython notebook uses.")
384 "IPython notebook uses.")
385
385
386 enable_mathjax = Bool(True, config=True,
386 enable_mathjax = Bool(True, config=True,
387 help="""Whether to enable MathJax for typesetting math/TeX
387 help="""Whether to enable MathJax for typesetting math/TeX
388
388
389 MathJax is the javascript library IPython uses to render math/LaTeX. It is
389 MathJax is the javascript library IPython uses to render math/LaTeX. It is
390 very large, so you may want to disable it if you have a slow internet
390 very large, so you may want to disable it if you have a slow internet
391 connection, or for offline use of the notebook.
391 connection, or for offline use of the notebook.
392
392
393 When disabled, equations etc. will appear as their untransformed TeX source.
393 When disabled, equations etc. will appear as their untransformed TeX source.
394 """
394 """
395 )
395 )
396 def _enable_mathjax_changed(self, name, old, new):
396 def _enable_mathjax_changed(self, name, old, new):
397 """set mathjax url to empty if mathjax is disabled"""
397 """set mathjax url to empty if mathjax is disabled"""
398 if not new:
398 if not new:
399 self.mathjax_url = u''
399 self.mathjax_url = u''
400
400
401 base_project_url = Unicode('/', config=True,
401 base_project_url = Unicode('/', config=True,
402 help='''The base URL for the notebook server.
402 help='''The base URL for the notebook server.
403
403
404 Leading and trailing slashes can be omitted,
404 Leading and trailing slashes can be omitted,
405 and will automatically be added.
405 and will automatically be added.
406 ''')
406 ''')
407 def _base_project_url_changed(self, name, old, new):
407 def _base_project_url_changed(self, name, old, new):
408 if not new.startswith('/'):
408 if not new.startswith('/'):
409 self.base_project_url = '/'+new
409 self.base_project_url = '/'+new
410 elif not new.endswith('/'):
410 elif not new.endswith('/'):
411 self.base_project_url = new+'/'
411 self.base_project_url = new+'/'
412
412
413 base_kernel_url = Unicode('/', config=True,
413 base_kernel_url = Unicode('/', config=True,
414 help='''The base URL for the kernel server
414 help='''The base URL for the kernel server
415
415
416 Leading and trailing slashes can be omitted,
416 Leading and trailing slashes can be omitted,
417 and will automatically be added.
417 and will automatically be added.
418 ''')
418 ''')
419 def _base_kernel_url_changed(self, name, old, new):
419 def _base_kernel_url_changed(self, name, old, new):
420 if not new.startswith('/'):
420 if not new.startswith('/'):
421 self.base_kernel_url = '/'+new
421 self.base_kernel_url = '/'+new
422 elif not new.endswith('/'):
422 elif not new.endswith('/'):
423 self.base_kernel_url = new+'/'
423 self.base_kernel_url = new+'/'
424
424
425 websocket_host = Unicode("", config=True,
425 websocket_host = Unicode("", config=True,
426 help="""The hostname for the websocket server."""
426 help="""The hostname for the websocket server."""
427 )
427 )
428
428
429 extra_static_paths = List(Unicode, config=True,
429 extra_static_paths = List(Unicode, config=True,
430 help="""Extra paths to search for serving static files.
430 help="""Extra paths to search for serving static files.
431
431
432 This allows adding javascript/css to be available from the notebook server machine,
432 This allows adding javascript/css to be available from the notebook server machine,
433 or overriding individual files in the IPython"""
433 or overriding individual files in the IPython"""
434 )
434 )
435 def _extra_static_paths_default(self):
435 def _extra_static_paths_default(self):
436 return [os.path.join(self.profile_dir.location, 'static')]
436 return [os.path.join(self.profile_dir.location, 'static')]
437
437
438 @property
438 @property
439 def static_file_path(self):
439 def static_file_path(self):
440 """return extra paths + the default location"""
440 """return extra paths + the default location"""
441 return self.extra_static_paths + [DEFAULT_STATIC_FILES_PATH]
441 return self.extra_static_paths + [DEFAULT_STATIC_FILES_PATH]
442
442
443 mathjax_url = Unicode("", config=True,
443 mathjax_url = Unicode("", config=True,
444 help="""The url for MathJax.js."""
444 help="""The url for MathJax.js."""
445 )
445 )
446 def _mathjax_url_default(self):
446 def _mathjax_url_default(self):
447 if not self.enable_mathjax:
447 if not self.enable_mathjax:
448 return u''
448 return u''
449 static_url_prefix = self.webapp_settings.get("static_url_prefix",
449 static_url_prefix = self.webapp_settings.get("static_url_prefix",
450 "/static/")
450 "/static/")
451 try:
451 try:
452 mathjax = filefind(os.path.join('mathjax', 'MathJax.js'), self.static_file_path)
452 mathjax = filefind(os.path.join('mathjax', 'MathJax.js'), self.static_file_path)
453 except IOError:
453 except IOError:
454 if self.certfile:
454 if self.certfile:
455 # HTTPS: load from Rackspace CDN, because SSL certificate requires it
455 # HTTPS: load from Rackspace CDN, because SSL certificate requires it
456 base = u"https://c328740.ssl.cf1.rackcdn.com"
456 base = u"https://c328740.ssl.cf1.rackcdn.com"
457 else:
457 else:
458 base = u"http://cdn.mathjax.org"
458 base = u"http://cdn.mathjax.org"
459
459
460 url = base + u"/mathjax/latest/MathJax.js"
460 url = base + u"/mathjax/latest/MathJax.js"
461 self.log.info("Using MathJax from CDN: %s", url)
461 self.log.info("Using MathJax from CDN: %s", url)
462 return url
462 return url
463 else:
463 else:
464 self.log.info("Using local MathJax from %s" % mathjax)
464 self.log.info("Using local MathJax from %s" % mathjax)
465 return static_url_prefix+u"mathjax/MathJax.js"
465 return static_url_prefix+u"mathjax/MathJax.js"
466
466
467 def _mathjax_url_changed(self, name, old, new):
467 def _mathjax_url_changed(self, name, old, new):
468 if new and not self.enable_mathjax:
468 if new and not self.enable_mathjax:
469 # enable_mathjax=False overrides mathjax_url
469 # enable_mathjax=False overrides mathjax_url
470 self.mathjax_url = u''
470 self.mathjax_url = u''
471 else:
471 else:
472 self.log.info("Using MathJax: %s", new)
472 self.log.info("Using MathJax: %s", new)
473
473
474 notebook_manager_class = DottedObjectName('IPython.frontend.html.notebook.filenbmanager.FileNotebookManager',
474 notebook_manager_class = DottedObjectName('IPython.frontend.html.notebook.filenbmanager.FileNotebookManager',
475 config=True,
475 config=True,
476 help='The notebook manager class to use.')
476 help='The notebook manager class to use.')
477
477
478 trust_xheaders = Bool(False, config=True,
478 trust_xheaders = Bool(False, config=True,
479 help=("Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headers"
479 help=("Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headers"
480 "sent by the upstream reverse proxy. Neccesary if the proxy handles SSL")
480 "sent by the upstream reverse proxy. Neccesary if the proxy handles SSL")
481 )
481 )
482
482
483 def parse_command_line(self, argv=None):
483 def parse_command_line(self, argv=None):
484 super(NotebookApp, self).parse_command_line(argv)
484 super(NotebookApp, self).parse_command_line(argv)
485 if argv is None:
485 if argv is None:
486 argv = sys.argv[1:]
486 argv = sys.argv[1:]
487
487
488 # Scrub frontend-specific flags
488 # Scrub frontend-specific flags
489 self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags)
489 self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags)
490 # Kernel should inherit default config file from frontend
490 # Kernel should inherit default config file from frontend
491 self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name)
491 self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name)
492
492
493 if self.extra_args:
493 if self.extra_args:
494 f = os.path.abspath(self.extra_args[0])
494 f = os.path.abspath(self.extra_args[0])
495 if os.path.isdir(f):
495 if os.path.isdir(f):
496 nbdir = f
496 nbdir = f
497 else:
497 else:
498 self.file_to_run = f
498 self.file_to_run = f
499 nbdir = os.path.dirname(f)
499 nbdir = os.path.dirname(f)
500 self.config.NotebookManager.notebook_dir = nbdir
500 self.config.NotebookManager.notebook_dir = nbdir
501
501
502 def init_configurables(self):
502 def init_configurables(self):
503 # force Session default to be secure
503 # force Session default to be secure
504 default_secure(self.config)
504 default_secure(self.config)
505 self.kernel_manager = MappingKernelManager(
505 self.kernel_manager = MappingKernelManager(
506 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
506 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
507 connection_dir = self.profile_dir.security_dir,
507 connection_dir = self.profile_dir.security_dir,
508 )
508 )
509 kls = import_item(self.notebook_manager_class)
509 kls = import_item(self.notebook_manager_class)
510 self.notebook_manager = kls(config=self.config, log=self.log)
510 self.notebook_manager = kls(config=self.config, log=self.log)
511 self.notebook_manager.load_notebook_names()
511 self.notebook_manager.load_notebook_names()
512 self.cluster_manager = ClusterManager(config=self.config, log=self.log)
512 self.cluster_manager = ClusterManager(config=self.config, log=self.log)
513 self.cluster_manager.update_profiles()
513 self.cluster_manager.update_profiles()
514
514
515 def init_logging(self):
515 def init_logging(self):
516 # This prevents double log messages because tornado use a root logger that
516 # This prevents double log messages because tornado use a root logger that
517 # self.log is a child of. The logging module dipatches log messages to a log
517 # self.log is a child of. The logging module dipatches log messages to a log
518 # and all of its ancenstors until propagate is set to False.
518 # and all of its ancenstors until propagate is set to False.
519 self.log.propagate = False
519 self.log.propagate = False
520
520
521 def init_webapp(self):
521 def init_webapp(self):
522 """initialize tornado webapp and httpserver"""
522 """initialize tornado webapp and httpserver"""
523 self.web_app = NotebookWebApplication(
523 self.web_app = NotebookWebApplication(
524 self, self.kernel_manager, self.notebook_manager,
524 self, self.kernel_manager, self.notebook_manager,
525 self.cluster_manager, self.log,
525 self.cluster_manager, self.log,
526 self.base_project_url, self.webapp_settings
526 self.base_project_url, self.webapp_settings
527 )
527 )
528 if self.certfile:
528 if self.certfile:
529 ssl_options = dict(certfile=self.certfile)
529 ssl_options = dict(certfile=self.certfile)
530 if self.keyfile:
530 if self.keyfile:
531 ssl_options['keyfile'] = self.keyfile
531 ssl_options['keyfile'] = self.keyfile
532 else:
532 else:
533 ssl_options = None
533 ssl_options = None
534 self.web_app.password = self.password
534 self.web_app.password = self.password
535 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options,
535 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options,
536 xheaders=self.trust_xheaders)
536 xheaders=self.trust_xheaders)
537 if not self.ip:
537 if not self.ip:
538 warning = "WARNING: The notebook server is listening on all IP addresses"
538 warning = "WARNING: The notebook server is listening on all IP addresses"
539 if ssl_options is None:
539 if ssl_options is None:
540 self.log.critical(warning + " and not using encryption. This"
540 self.log.critical(warning + " and not using encryption. This"
541 "is not recommended.")
541 "is not recommended.")
542 if not self.password and not self.read_only:
542 if not self.password and not self.read_only:
543 self.log.critical(warning + "and not using authentication."
543 self.log.critical(warning + "and not using authentication."
544 "This is highly insecure and not recommended.")
544 "This is highly insecure and not recommended.")
545 success = None
545 success = None
546 for port in random_ports(self.port, self.port_retries+1):
546 for port in random_ports(self.port, self.port_retries+1):
547 try:
547 try:
548 self.http_server.listen(port, self.ip)
548 self.http_server.listen(port, self.ip)
549 except socket.error as e:
549 except socket.error as e:
550 # XXX: remove the e.errno == -9 block when we require
550 # XXX: remove the e.errno == -9 block when we require
551 # tornado >= 3.0
551 # tornado >= 3.0
552 if e.errno == -9 and tornado.version_info[0] < 3:
552 if e.errno == -9 and tornado.version_info[0] < 3:
553 # The flags passed to socket.getaddrinfo from
553 # The flags passed to socket.getaddrinfo from
554 # tornado.netutils.bind_sockets can cause "gaierror:
554 # tornado.netutils.bind_sockets can cause "gaierror:
555 # [Errno -9] Address family for hostname not supported"
555 # [Errno -9] Address family for hostname not supported"
556 # when the interface is not associated, for example.
556 # when the interface is not associated, for example.
557 # Changing the flags to exclude socket.AI_ADDRCONFIG does
557 # Changing the flags to exclude socket.AI_ADDRCONFIG does
558 # not cause this error, but the only way to do this is to
558 # not cause this error, but the only way to do this is to
559 # monkeypatch socket to remove the AI_ADDRCONFIG attribute
559 # monkeypatch socket to remove the AI_ADDRCONFIG attribute
560 saved_AI_ADDRCONFIG = socket.AI_ADDRCONFIG
560 saved_AI_ADDRCONFIG = socket.AI_ADDRCONFIG
561 self.log.warn('Monkeypatching socket to fix tornado bug')
561 self.log.warn('Monkeypatching socket to fix tornado bug')
562 del(socket.AI_ADDRCONFIG)
562 del(socket.AI_ADDRCONFIG)
563 try:
563 try:
564 # retry the tornado call without AI_ADDRCONFIG flags
564 # retry the tornado call without AI_ADDRCONFIG flags
565 self.http_server.listen(port, self.ip)
565 self.http_server.listen(port, self.ip)
566 except socket.error as e2:
566 except socket.error as e2:
567 e = e2
567 e = e2
568 else:
568 else:
569 self.port = port
569 self.port = port
570 success = True
570 success = True
571 break
571 break
572 # restore the monekypatch
572 # restore the monekypatch
573 socket.AI_ADDRCONFIG = saved_AI_ADDRCONFIG
573 socket.AI_ADDRCONFIG = saved_AI_ADDRCONFIG
574 if e.errno != errno.EADDRINUSE:
574 if e.errno != errno.EADDRINUSE:
575 raise
575 raise
576 self.log.info('The port %i is already in use, trying another random port.' % port)
576 self.log.info('The port %i is already in use, trying another random port.' % port)
577 else:
577 else:
578 self.port = port
578 self.port = port
579 success = True
579 success = True
580 break
580 break
581 if not success:
581 if not success:
582 self.log.critical('ERROR: the notebook server could not be started because '
582 self.log.critical('ERROR: the notebook server could not be started because '
583 'no available port could be found.')
583 'no available port could be found.')
584 self.exit(1)
584 self.exit(1)
585
585
586 def init_signal(self):
586 def init_signal(self):
587 if not sys.platform.startswith('win'):
587 if not sys.platform.startswith('win'):
588 signal.signal(signal.SIGINT, self._handle_sigint)
588 signal.signal(signal.SIGINT, self._handle_sigint)
589 signal.signal(signal.SIGTERM, self._signal_stop)
589 signal.signal(signal.SIGTERM, self._signal_stop)
590 if hasattr(signal, 'SIGUSR1'):
590 if hasattr(signal, 'SIGUSR1'):
591 # Windows doesn't support SIGUSR1
591 # Windows doesn't support SIGUSR1
592 signal.signal(signal.SIGUSR1, self._signal_info)
592 signal.signal(signal.SIGUSR1, self._signal_info)
593 if hasattr(signal, 'SIGINFO'):
593 if hasattr(signal, 'SIGINFO'):
594 # only on BSD-based systems
594 # only on BSD-based systems
595 signal.signal(signal.SIGINFO, self._signal_info)
595 signal.signal(signal.SIGINFO, self._signal_info)
596
596
597 def _handle_sigint(self, sig, frame):
597 def _handle_sigint(self, sig, frame):
598 """SIGINT handler spawns confirmation dialog"""
598 """SIGINT handler spawns confirmation dialog"""
599 # register more forceful signal handler for ^C^C case
599 # register more forceful signal handler for ^C^C case
600 signal.signal(signal.SIGINT, self._signal_stop)
600 signal.signal(signal.SIGINT, self._signal_stop)
601 # request confirmation dialog in bg thread, to avoid
601 # request confirmation dialog in bg thread, to avoid
602 # blocking the App
602 # blocking the App
603 thread = threading.Thread(target=self._confirm_exit)
603 thread = threading.Thread(target=self._confirm_exit)
604 thread.daemon = True
604 thread.daemon = True
605 thread.start()
605 thread.start()
606
606
607 def _restore_sigint_handler(self):
607 def _restore_sigint_handler(self):
608 """callback for restoring original SIGINT handler"""
608 """callback for restoring original SIGINT handler"""
609 signal.signal(signal.SIGINT, self._handle_sigint)
609 signal.signal(signal.SIGINT, self._handle_sigint)
610
610
611 def _confirm_exit(self):
611 def _confirm_exit(self):
612 """confirm shutdown on ^C
612 """confirm shutdown on ^C
613
613
614 A second ^C, or answering 'y' within 5s will cause shutdown,
614 A second ^C, or answering 'y' within 5s will cause shutdown,
615 otherwise original SIGINT handler will be restored.
615 otherwise original SIGINT handler will be restored.
616
616
617 This doesn't work on Windows.
617 This doesn't work on Windows.
618 """
618 """
619 # FIXME: remove this delay when pyzmq dependency is >= 2.1.11
619 # FIXME: remove this delay when pyzmq dependency is >= 2.1.11
620 time.sleep(0.1)
620 time.sleep(0.1)
621 info = self.log.info
621 info = self.log.info
622 info('interrupted')
622 info('interrupted')
623 print self.notebook_info()
623 print self.notebook_info()
624 sys.stdout.write("Shutdown this notebook server (y/[n])? ")
624 sys.stdout.write("Shutdown this notebook server (y/[n])? ")
625 sys.stdout.flush()
625 sys.stdout.flush()
626 r,w,x = select.select([sys.stdin], [], [], 5)
626 r,w,x = select.select([sys.stdin], [], [], 5)
627 if r:
627 if r:
628 line = sys.stdin.readline()
628 line = sys.stdin.readline()
629 if line.lower().startswith('y'):
629 if line.lower().startswith('y'):
630 self.log.critical("Shutdown confirmed")
630 self.log.critical("Shutdown confirmed")
631 ioloop.IOLoop.instance().stop()
631 ioloop.IOLoop.instance().stop()
632 return
632 return
633 else:
633 else:
634 print "No answer for 5s:",
634 print "No answer for 5s:",
635 print "resuming operation..."
635 print "resuming operation..."
636 # no answer, or answer is no:
636 # no answer, or answer is no:
637 # set it back to original SIGINT handler
637 # set it back to original SIGINT handler
638 # use IOLoop.add_callback because signal.signal must be called
638 # use IOLoop.add_callback because signal.signal must be called
639 # from main thread
639 # from main thread
640 ioloop.IOLoop.instance().add_callback(self._restore_sigint_handler)
640 ioloop.IOLoop.instance().add_callback(self._restore_sigint_handler)
641
641
642 def _signal_stop(self, sig, frame):
642 def _signal_stop(self, sig, frame):
643 self.log.critical("received signal %s, stopping", sig)
643 self.log.critical("received signal %s, stopping", sig)
644 ioloop.IOLoop.instance().stop()
644 ioloop.IOLoop.instance().stop()
645
645
646 def _signal_info(self, sig, frame):
646 def _signal_info(self, sig, frame):
647 print self.notebook_info()
647 print self.notebook_info()
648
648
649 @catch_config_error
649 @catch_config_error
650 def initialize(self, argv=None):
650 def initialize(self, argv=None):
651 self.init_logging()
651 self.init_logging()
652 super(NotebookApp, self).initialize(argv)
652 super(NotebookApp, self).initialize(argv)
653 self.init_configurables()
653 self.init_configurables()
654 self.init_webapp()
654 self.init_webapp()
655 self.init_signal()
655 self.init_signal()
656
656
657 def cleanup_kernels(self):
657 def cleanup_kernels(self):
658 """Shutdown all kernels.
658 """Shutdown all kernels.
659
659
660 The kernels will shutdown themselves when this process no longer exists,
660 The kernels will shutdown themselves when this process no longer exists,
661 but explicit shutdown allows the KernelManagers to cleanup the connection files.
661 but explicit shutdown allows the KernelManagers to cleanup the connection files.
662 """
662 """
663 self.log.info('Shutting down kernels')
663 self.log.info('Shutting down kernels')
664 self.kernel_manager.shutdown_all()
664 self.kernel_manager.shutdown_all()
665
665
666 def notebook_info(self):
666 def notebook_info(self):
667 "Return the current working directory and the server url information"
667 "Return the current working directory and the server url information"
668 mgr_info = self.notebook_manager.info_string() + "\n"
668 mgr_info = self.notebook_manager.info_string() + "\n"
669 return mgr_info +"The IPython Notebook is running at: %s" % self._url
669 return mgr_info +"The IPython Notebook is running at: %s" % self._url
670
670
671 def start(self):
671 def start(self):
672 """ Start the IPython Notebok server app, after initialization
672 """ Start the IPython Notebok server app, after initialization
673
673
674 This method takes no arguments so all configuration and initialization
674 This method takes no arguments so all configuration and initialization
675 must be done prior to calling this method."""
675 must be done prior to calling this method."""
676 ip = self.ip if self.ip else '[all ip addresses on your system]'
676 ip = self.ip if self.ip else '[all ip addresses on your system]'
677 proto = 'https' if self.certfile else 'http'
677 proto = 'https' if self.certfile else 'http'
678 info = self.log.info
678 info = self.log.info
679 self._url = "%s://%s:%i%s" % (proto, ip, self.port,
679 self._url = "%s://%s:%i%s" % (proto, ip, self.port,
680 self.base_project_url)
680 self.base_project_url)
681 for line in self.notebook_info().split("\n"):
681 for line in self.notebook_info().split("\n"):
682 info(line)
682 info(line)
683 info("Use Control-C to stop this server and shut down all kernels.")
683 info("Use Control-C to stop this server and shut down all kernels.")
684
684
685 if self.open_browser or self.file_to_run:
685 if self.open_browser or self.file_to_run:
686 ip = self.ip or LOCALHOST
686 ip = self.ip or LOCALHOST
687 try:
687 try:
688 browser = webbrowser.get(self.browser or None)
688 browser = webbrowser.get(self.browser or None)
689 except webbrowser.Error as e:
689 except webbrowser.Error as e:
690 self.log.warn('No web browser found: %s.' % e)
690 self.log.warn('No web browser found: %s.' % e)
691 browser = None
691 browser = None
692
692
693 if self.file_to_run:
693 if self.file_to_run:
694 name, _ = os.path.splitext(os.path.basename(self.file_to_run))
694 name, _ = os.path.splitext(os.path.basename(self.file_to_run))
695 url = self.notebook_manager.rev_mapping.get(name, '')
695 url = self.notebook_manager.rev_mapping.get(name, '')
696 else:
696 else:
697 url = ''
697 url = ''
698 if browser:
698 if browser:
699 b = lambda : browser.open("%s://%s:%i%s%s" % (proto, ip,
699 b = lambda : browser.open("%s://%s:%i%s%s" % (proto, ip,
700 self.port, self.base_project_url, url), new=2)
700 self.port, self.base_project_url, url), new=2)
701 threading.Thread(target=b).start()
701 threading.Thread(target=b).start()
702 try:
702 try:
703 ioloop.IOLoop.instance().start()
703 ioloop.IOLoop.instance().start()
704 except KeyboardInterrupt:
704 except KeyboardInterrupt:
705 info("Interrupted...")
705 info("Interrupted...")
706 finally:
706 finally:
707 self.cleanup_kernels()
707 self.cleanup_kernels()
708
708
709
709
710 #-----------------------------------------------------------------------------
710 #-----------------------------------------------------------------------------
711 # Main entry point
711 # Main entry point
712 #-----------------------------------------------------------------------------
712 #-----------------------------------------------------------------------------
713
713
714 def launch_new_instance():
714 def launch_new_instance():
715 app = NotebookApp.instance()
715 app = NotebookApp.instance()
716 app.initialize()
716 app.initialize()
717 app.start()
717 app.start()
718
718
@@ -1,49 +1,17 b''
1 #-----------------------------------------------------------------------------
1 #-----------------------------------------------------------------------------
2 # Copyright (C) 2010 The IPython Development Team
2 # Copyright (C) 2013 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.txt, distributed as part of this software.
5 # the file COPYING.txt, distributed as part of this software.
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Verify zmq version dependency >= 2.1.11
9 # Verify zmq version dependency >= 2.1.11
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 import warnings
12 from IPython.utils.zmqrelated import check_for_zmq
13 from IPython.utils.version import check_version
14
13
15
14 check_for_zmq('2.1.11', 'IPython.kernel.zmq')
16 def patch_pyzmq():
17 """backport a few patches from newer pyzmq
18
19 These can be removed as we bump our minimum pyzmq version
20 """
21
22 import zmq
23
24 # fallback on stdlib json if jsonlib is selected, because jsonlib breaks things.
25 # jsonlib support is removed from pyzmq >= 2.2.0
26
27 from zmq.utils import jsonapi
28 if jsonapi.jsonmod.__name__ == 'jsonlib':
29 import json
30 jsonapi.jsonmod = json
31
32
33 def check_for_zmq(minimum_version, module='IPython.kernel.zmq'):
34 try:
35 import zmq
36 except ImportError:
37 raise ImportError("%s requires pyzmq >= %s"%(module, minimum_version))
38
39 pyzmq_version = zmq.__version__
40
41 if not check_version(pyzmq_version, minimum_version):
42 raise ImportError("%s requires pyzmq >= %s, but you have %s"%(
43 module, minimum_version, pyzmq_version))
44
45 check_for_zmq('2.1.11')
46 patch_pyzmq()
47
15
48 from .session import Session
16 from .session import Session
49
17
@@ -1,72 +1,72 b''
1 """The IPython ZMQ-based parallel computing interface.
1 """The IPython ZMQ-based parallel computing interface.
2
2
3 Authors:
3 Authors:
4
4
5 * MinRK
5 * MinRK
6 """
6 """
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2011 The IPython Development Team
8 # Copyright (C) 2011 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 import os
18 import os
19 import warnings
19 import warnings
20
20
21 import zmq
21 import zmq
22
22
23 from IPython.config.configurable import MultipleInstanceError
23 from IPython.config.configurable import MultipleInstanceError
24 from IPython.kernel.zmq import check_for_zmq
24 from IPython.utils.zmqrelated import check_for_zmq
25
25
26 min_pyzmq = '2.1.11'
26 min_pyzmq = '2.1.11'
27
27
28 check_for_zmq(min_pyzmq, 'IPython.parallel')
28 check_for_zmq(min_pyzmq, 'IPython.parallel')
29
29
30 from IPython.utils.pickleutil import Reference
30 from IPython.utils.pickleutil import Reference
31
31
32 from .client.asyncresult import *
32 from .client.asyncresult import *
33 from .client.client import Client
33 from .client.client import Client
34 from .client.remotefunction import *
34 from .client.remotefunction import *
35 from .client.view import *
35 from .client.view import *
36 from .controller.dependency import *
36 from .controller.dependency import *
37 from .error import *
37 from .error import *
38 from .util import interactive
38 from .util import interactive
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Functions
41 # Functions
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43
44
44
45 def bind_kernel(**kwargs):
45 def bind_kernel(**kwargs):
46 """Bind an Engine's Kernel to be used as a full IPython kernel.
46 """Bind an Engine's Kernel to be used as a full IPython kernel.
47
47
48 This allows a running Engine to be used simultaneously as a full IPython kernel
48 This allows a running Engine to be used simultaneously as a full IPython kernel
49 with the QtConsole or other frontends.
49 with the QtConsole or other frontends.
50
50
51 This function returns immediately.
51 This function returns immediately.
52 """
52 """
53 from IPython.kernel.zmq.kernelapp import IPKernelApp
53 from IPython.kernel.zmq.kernelapp import IPKernelApp
54 from IPython.parallel.apps.ipengineapp import IPEngineApp
54 from IPython.parallel.apps.ipengineapp import IPEngineApp
55
55
56 # first check for IPKernelApp, in which case this should be a no-op
56 # first check for IPKernelApp, in which case this should be a no-op
57 # because there is already a bound kernel
57 # because there is already a bound kernel
58 if IPKernelApp.initialized() and isinstance(IPKernelApp._instance, IPKernelApp):
58 if IPKernelApp.initialized() and isinstance(IPKernelApp._instance, IPKernelApp):
59 return
59 return
60
60
61 if IPEngineApp.initialized():
61 if IPEngineApp.initialized():
62 try:
62 try:
63 app = IPEngineApp.instance()
63 app = IPEngineApp.instance()
64 except MultipleInstanceError:
64 except MultipleInstanceError:
65 pass
65 pass
66 else:
66 else:
67 return app.bind_kernel(**kwargs)
67 return app.bind_kernel(**kwargs)
68
68
69 raise RuntimeError("bind_kernel be called from an IPEngineApp instance")
69 raise RuntimeError("bind_kernel be called from an IPEngineApp instance")
70
70
71
71
72
72
General Comments 0
You need to be logged in to leave comments. Login now