##// END OF EJS Templates
Rename texteditor files & folders to edit
Thomas Kluyver -
Show More
1 NO CONTENT: file renamed from IPython/html/texteditor/__init__.py to IPython/html/edit/__init__.py
@@ -1,29 +1,29 b''
1 1 #encoding: utf-8
2 2 """Tornado handlers for the terminal emulator."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 from tornado import web
8 8 from ..base.handlers import IPythonHandler, path_regex
9 9 from ..utils import url_escape
10 10
11 11 class EditorHandler(IPythonHandler):
12 12 """Render the text editor interface."""
13 13 @web.authenticated
14 14 def get(self, path):
15 15 path = path.strip('/')
16 16 if not self.contents_manager.file_exists(path):
17 17 raise web.HTTPError(404, u'File does not exist: %s' % path)
18 18
19 19 basename = path.rsplit('/', 1)[-1]
20 self.write(self.render_template('texteditor.html',
20 self.write(self.render_template('edit.html',
21 21 file_path=url_escape(path),
22 22 basename=basename,
23 23 page_title=basename + " (editing)",
24 24 )
25 25 )
26 26
27 27 default_handlers = [
28 28 (r"/edit%s" % path_regex, EditorHandler),
29 29 ] No newline at end of file
@@ -1,995 +1,995 b''
1 1 # coding: utf-8
2 2 """A tornado based IPython notebook server."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 from __future__ import print_function
8 8
9 9 import base64
10 10 import errno
11 11 import io
12 12 import json
13 13 import logging
14 14 import os
15 15 import random
16 16 import re
17 17 import select
18 18 import signal
19 19 import socket
20 20 import sys
21 21 import threading
22 22 import time
23 23 import webbrowser
24 24
25 25
26 26 # check for pyzmq 2.1.11
27 27 from IPython.utils.zmqrelated import check_for_zmq
28 28 check_for_zmq('2.1.11', 'IPython.html')
29 29
30 30 from jinja2 import Environment, FileSystemLoader
31 31
32 32 # Install the pyzmq ioloop. This has to be done before anything else from
33 33 # tornado is imported.
34 34 from zmq.eventloop import ioloop
35 35 ioloop.install()
36 36
37 37 # check for tornado 3.1.0
38 38 msg = "The IPython Notebook requires tornado >= 4.0"
39 39 try:
40 40 import tornado
41 41 except ImportError:
42 42 raise ImportError(msg)
43 43 try:
44 44 version_info = tornado.version_info
45 45 except AttributeError:
46 46 raise ImportError(msg + ", but you have < 1.1.0")
47 47 if version_info < (4,0):
48 48 raise ImportError(msg + ", but you have %s" % tornado.version)
49 49
50 50 from tornado import httpserver
51 51 from tornado import web
52 52 from tornado.log import LogFormatter, app_log, access_log, gen_log
53 53
54 54 from IPython.html import (
55 55 DEFAULT_STATIC_FILES_PATH,
56 56 DEFAULT_TEMPLATE_PATH_LIST,
57 57 )
58 58 from .base.handlers import Template404
59 59 from .log import log_request
60 60 from .services.kernels.kernelmanager import MappingKernelManager
61 61 from .services.contents.manager import ContentsManager
62 62 from .services.contents.filemanager import FileContentsManager
63 63 from .services.clusters.clustermanager import ClusterManager
64 64 from .services.sessions.sessionmanager import SessionManager
65 65
66 66 from .base.handlers import AuthenticatedFileHandler, FileFindHandler
67 67
68 68 from IPython.config import Config
69 69 from IPython.config.application import catch_config_error, boolean_flag
70 70 from IPython.core.application import (
71 71 BaseIPythonApplication, base_flags, base_aliases,
72 72 )
73 73 from IPython.core.profiledir import ProfileDir
74 74 from IPython.kernel import KernelManager
75 75 from IPython.kernel.kernelspec import KernelSpecManager
76 76 from IPython.kernel.zmq.session import default_secure, Session
77 77 from IPython.nbformat.sign import NotebookNotary
78 78 from IPython.utils.importstring import import_item
79 79 from IPython.utils import submodule
80 80 from IPython.utils.process import check_pid
81 81 from IPython.utils.traitlets import (
82 82 Dict, Unicode, Integer, List, Bool, Bytes, Instance,
83 83 DottedObjectName, TraitError,
84 84 )
85 85 from IPython.utils import py3compat
86 86 from IPython.utils.path import filefind, get_ipython_dir
87 87
88 88 from .utils import url_path_join
89 89
90 90 #-----------------------------------------------------------------------------
91 91 # Module globals
92 92 #-----------------------------------------------------------------------------
93 93
94 94 _examples = """
95 95 ipython notebook # start the notebook
96 96 ipython notebook --profile=sympy # use the sympy profile
97 97 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
98 98 """
99 99
100 100 #-----------------------------------------------------------------------------
101 101 # Helper functions
102 102 #-----------------------------------------------------------------------------
103 103
104 104 def random_ports(port, n):
105 105 """Generate a list of n random ports near the given port.
106 106
107 107 The first 5 ports will be sequential, and the remaining n-5 will be
108 108 randomly selected in the range [port-2*n, port+2*n].
109 109 """
110 110 for i in range(min(5, n)):
111 111 yield port + i
112 112 for i in range(n-5):
113 113 yield max(1, port + random.randint(-2*n, 2*n))
114 114
115 115 def load_handlers(name):
116 116 """Load the (URL pattern, handler) tuples for each component."""
117 117 name = 'IPython.html.' + name
118 118 mod = __import__(name, fromlist=['default_handlers'])
119 119 return mod.default_handlers
120 120
121 121 #-----------------------------------------------------------------------------
122 122 # The Tornado web application
123 123 #-----------------------------------------------------------------------------
124 124
125 125 class NotebookWebApplication(web.Application):
126 126
127 127 def __init__(self, ipython_app, kernel_manager, contents_manager,
128 128 cluster_manager, session_manager, kernel_spec_manager, log,
129 129 base_url, default_url, settings_overrides, jinja_env_options):
130 130
131 131 settings = self.init_settings(
132 132 ipython_app, kernel_manager, contents_manager, cluster_manager,
133 133 session_manager, kernel_spec_manager, log, base_url, default_url,
134 134 settings_overrides, jinja_env_options)
135 135 handlers = self.init_handlers(settings)
136 136
137 137 super(NotebookWebApplication, self).__init__(handlers, **settings)
138 138
139 139 def init_settings(self, ipython_app, kernel_manager, contents_manager,
140 140 cluster_manager, session_manager, kernel_spec_manager,
141 141 log, base_url, default_url, settings_overrides,
142 142 jinja_env_options=None):
143 143
144 144 _template_path = settings_overrides.get(
145 145 "template_path",
146 146 ipython_app.template_file_path,
147 147 )
148 148 if isinstance(_template_path, str):
149 149 _template_path = (_template_path,)
150 150 template_path = [os.path.expanduser(path) for path in _template_path]
151 151
152 152 jenv_opt = jinja_env_options if jinja_env_options else {}
153 153 env = Environment(loader=FileSystemLoader(template_path), **jenv_opt)
154 154 settings = dict(
155 155 # basics
156 156 log_function=log_request,
157 157 base_url=base_url,
158 158 default_url=default_url,
159 159 template_path=template_path,
160 160 static_path=ipython_app.static_file_path,
161 161 static_handler_class = FileFindHandler,
162 162 static_url_prefix = url_path_join(base_url,'/static/'),
163 163
164 164 # authentication
165 165 cookie_secret=ipython_app.cookie_secret,
166 166 login_url=url_path_join(base_url,'/login'),
167 167 password=ipython_app.password,
168 168
169 169 # managers
170 170 kernel_manager=kernel_manager,
171 171 contents_manager=contents_manager,
172 172 cluster_manager=cluster_manager,
173 173 session_manager=session_manager,
174 174 kernel_spec_manager=kernel_spec_manager,
175 175
176 176 # IPython stuff
177 177 nbextensions_path = ipython_app.nbextensions_path,
178 178 websocket_url=ipython_app.websocket_url,
179 179 mathjax_url=ipython_app.mathjax_url,
180 180 config=ipython_app.config,
181 181 jinja2_env=env,
182 182 terminals_available=False, # Set later if terminals are available
183 183 profile_dir = ipython_app.profile_dir.location,
184 184 )
185 185
186 186 # allow custom overrides for the tornado web app.
187 187 settings.update(settings_overrides)
188 188 return settings
189 189
190 190 def init_handlers(self, settings):
191 191 """Load the (URL pattern, handler) tuples for each component."""
192 192
193 193 # Order matters. The first handler to match the URL will handle the request.
194 194 handlers = []
195 195 handlers.extend(load_handlers('tree.handlers'))
196 196 handlers.extend(load_handlers('auth.login'))
197 197 handlers.extend(load_handlers('auth.logout'))
198 198 handlers.extend(load_handlers('files.handlers'))
199 199 handlers.extend(load_handlers('notebook.handlers'))
200 200 handlers.extend(load_handlers('nbconvert.handlers'))
201 201 handlers.extend(load_handlers('kernelspecs.handlers'))
202 handlers.extend(load_handlers('texteditor.handlers'))
202 handlers.extend(load_handlers('edit.handlers'))
203 203 handlers.extend(load_handlers('services.config.handlers'))
204 204 handlers.extend(load_handlers('services.kernels.handlers'))
205 205 handlers.extend(load_handlers('services.contents.handlers'))
206 206 handlers.extend(load_handlers('services.clusters.handlers'))
207 207 handlers.extend(load_handlers('services.sessions.handlers'))
208 208 handlers.extend(load_handlers('services.nbconvert.handlers'))
209 209 handlers.extend(load_handlers('services.kernelspecs.handlers'))
210 210 handlers.append(
211 211 (r"/nbextensions/(.*)", FileFindHandler, {'path' : settings['nbextensions_path']}),
212 212 )
213 213 # register base handlers last
214 214 handlers.extend(load_handlers('base.handlers'))
215 215 # set the URL that will be redirected from `/`
216 216 handlers.append(
217 217 (r'/?', web.RedirectHandler, {
218 218 'url' : url_path_join(settings['base_url'], settings['default_url']),
219 219 'permanent': False, # want 302, not 301
220 220 })
221 221 )
222 222 # prepend base_url onto the patterns that we match
223 223 new_handlers = []
224 224 for handler in handlers:
225 225 pattern = url_path_join(settings['base_url'], handler[0])
226 226 new_handler = tuple([pattern] + list(handler[1:]))
227 227 new_handlers.append(new_handler)
228 228 # add 404 on the end, which will catch everything that falls through
229 229 new_handlers.append((r'(.*)', Template404))
230 230 return new_handlers
231 231
232 232
233 233 class NbserverListApp(BaseIPythonApplication):
234 234
235 235 description="List currently running notebook servers in this profile."
236 236
237 237 flags = dict(
238 238 json=({'NbserverListApp': {'json': True}},
239 239 "Produce machine-readable JSON output."),
240 240 )
241 241
242 242 json = Bool(False, config=True,
243 243 help="If True, each line of output will be a JSON object with the "
244 244 "details from the server info file.")
245 245
246 246 def start(self):
247 247 if not self.json:
248 248 print("Currently running servers:")
249 249 for serverinfo in list_running_servers(self.profile):
250 250 if self.json:
251 251 print(json.dumps(serverinfo))
252 252 else:
253 253 print(serverinfo['url'], "::", serverinfo['notebook_dir'])
254 254
255 255 #-----------------------------------------------------------------------------
256 256 # Aliases and Flags
257 257 #-----------------------------------------------------------------------------
258 258
259 259 flags = dict(base_flags)
260 260 flags['no-browser']=(
261 261 {'NotebookApp' : {'open_browser' : False}},
262 262 "Don't open the notebook in a browser after startup."
263 263 )
264 264 flags['pylab']=(
265 265 {'NotebookApp' : {'pylab' : 'warn'}},
266 266 "DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib."
267 267 )
268 268 flags['no-mathjax']=(
269 269 {'NotebookApp' : {'enable_mathjax' : False}},
270 270 """Disable MathJax
271 271
272 272 MathJax is the javascript library IPython uses to render math/LaTeX. It is
273 273 very large, so you may want to disable it if you have a slow internet
274 274 connection, or for offline use of the notebook.
275 275
276 276 When disabled, equations etc. will appear as their untransformed TeX source.
277 277 """
278 278 )
279 279
280 280 # Add notebook manager flags
281 281 flags.update(boolean_flag('script', 'FileContentsManager.save_script',
282 282 'DEPRECATED, IGNORED',
283 283 'DEPRECATED, IGNORED'))
284 284
285 285 aliases = dict(base_aliases)
286 286
287 287 aliases.update({
288 288 'ip': 'NotebookApp.ip',
289 289 'port': 'NotebookApp.port',
290 290 'port-retries': 'NotebookApp.port_retries',
291 291 'transport': 'KernelManager.transport',
292 292 'keyfile': 'NotebookApp.keyfile',
293 293 'certfile': 'NotebookApp.certfile',
294 294 'notebook-dir': 'NotebookApp.notebook_dir',
295 295 'browser': 'NotebookApp.browser',
296 296 'pylab': 'NotebookApp.pylab',
297 297 })
298 298
299 299 #-----------------------------------------------------------------------------
300 300 # NotebookApp
301 301 #-----------------------------------------------------------------------------
302 302
303 303 class NotebookApp(BaseIPythonApplication):
304 304
305 305 name = 'ipython-notebook'
306 306
307 307 description = """
308 308 The IPython HTML Notebook.
309 309
310 310 This launches a Tornado based HTML Notebook Server that serves up an
311 311 HTML5/Javascript Notebook client.
312 312 """
313 313 examples = _examples
314 314 aliases = aliases
315 315 flags = flags
316 316
317 317 classes = [
318 318 KernelManager, ProfileDir, Session, MappingKernelManager,
319 319 ContentsManager, FileContentsManager, NotebookNotary,
320 320 ]
321 321 flags = Dict(flags)
322 322 aliases = Dict(aliases)
323 323
324 324 subcommands = dict(
325 325 list=(NbserverListApp, NbserverListApp.description.splitlines()[0]),
326 326 )
327 327
328 328 ipython_kernel_argv = List(Unicode)
329 329
330 330 _log_formatter_cls = LogFormatter
331 331
332 332 def _log_level_default(self):
333 333 return logging.INFO
334 334
335 335 def _log_datefmt_default(self):
336 336 """Exclude date from default date format"""
337 337 return "%H:%M:%S"
338 338
339 339 def _log_format_default(self):
340 340 """override default log format to include time"""
341 341 return u"%(color)s[%(levelname)1.1s %(asctime)s.%(msecs).03d %(name)s]%(end_color)s %(message)s"
342 342
343 343 # create requested profiles by default, if they don't exist:
344 344 auto_create = Bool(True)
345 345
346 346 # file to be opened in the notebook server
347 347 file_to_run = Unicode('', config=True)
348 348
349 349 # Network related information
350 350
351 351 allow_origin = Unicode('', config=True,
352 352 help="""Set the Access-Control-Allow-Origin header
353 353
354 354 Use '*' to allow any origin to access your server.
355 355
356 356 Takes precedence over allow_origin_pat.
357 357 """
358 358 )
359 359
360 360 allow_origin_pat = Unicode('', config=True,
361 361 help="""Use a regular expression for the Access-Control-Allow-Origin header
362 362
363 363 Requests from an origin matching the expression will get replies with:
364 364
365 365 Access-Control-Allow-Origin: origin
366 366
367 367 where `origin` is the origin of the request.
368 368
369 369 Ignored if allow_origin is set.
370 370 """
371 371 )
372 372
373 373 allow_credentials = Bool(False, config=True,
374 374 help="Set the Access-Control-Allow-Credentials: true header"
375 375 )
376 376
377 377 default_url = Unicode('/tree', config=True,
378 378 help="The default URL to redirect to from `/`"
379 379 )
380 380
381 381 ip = Unicode('localhost', config=True,
382 382 help="The IP address the notebook server will listen on."
383 383 )
384 384
385 385 def _ip_changed(self, name, old, new):
386 386 if new == u'*': self.ip = u''
387 387
388 388 port = Integer(8888, config=True,
389 389 help="The port the notebook server will listen on."
390 390 )
391 391 port_retries = Integer(50, config=True,
392 392 help="The number of additional ports to try if the specified port is not available."
393 393 )
394 394
395 395 certfile = Unicode(u'', config=True,
396 396 help="""The full path to an SSL/TLS certificate file."""
397 397 )
398 398
399 399 keyfile = Unicode(u'', config=True,
400 400 help="""The full path to a private key file for usage with SSL/TLS."""
401 401 )
402 402
403 403 cookie_secret_file = Unicode(config=True,
404 404 help="""The file where the cookie secret is stored."""
405 405 )
406 406 def _cookie_secret_file_default(self):
407 407 if self.profile_dir is None:
408 408 return ''
409 409 return os.path.join(self.profile_dir.security_dir, 'notebook_cookie_secret')
410 410
411 411 cookie_secret = Bytes(b'', config=True,
412 412 help="""The random bytes used to secure cookies.
413 413 By default this is a new random number every time you start the Notebook.
414 414 Set it to a value in a config file to enable logins to persist across server sessions.
415 415
416 416 Note: Cookie secrets should be kept private, do not share config files with
417 417 cookie_secret stored in plaintext (you can read the value from a file).
418 418 """
419 419 )
420 420 def _cookie_secret_default(self):
421 421 if os.path.exists(self.cookie_secret_file):
422 422 with io.open(self.cookie_secret_file, 'rb') as f:
423 423 return f.read()
424 424 else:
425 425 secret = base64.encodestring(os.urandom(1024))
426 426 self._write_cookie_secret_file(secret)
427 427 return secret
428 428
429 429 def _write_cookie_secret_file(self, secret):
430 430 """write my secret to my secret_file"""
431 431 self.log.info("Writing notebook server cookie secret to %s", self.cookie_secret_file)
432 432 with io.open(self.cookie_secret_file, 'wb') as f:
433 433 f.write(secret)
434 434 try:
435 435 os.chmod(self.cookie_secret_file, 0o600)
436 436 except OSError:
437 437 self.log.warn(
438 438 "Could not set permissions on %s",
439 439 self.cookie_secret_file
440 440 )
441 441
442 442 password = Unicode(u'', config=True,
443 443 help="""Hashed password to use for web authentication.
444 444
445 445 To generate, type in a python/IPython shell:
446 446
447 447 from IPython.lib import passwd; passwd()
448 448
449 449 The string should be of the form type:salt:hashed-password.
450 450 """
451 451 )
452 452
453 453 open_browser = Bool(True, config=True,
454 454 help="""Whether to open in a browser after starting.
455 455 The specific browser used is platform dependent and
456 456 determined by the python standard library `webbrowser`
457 457 module, unless it is overridden using the --browser
458 458 (NotebookApp.browser) configuration option.
459 459 """)
460 460
461 461 browser = Unicode(u'', config=True,
462 462 help="""Specify what command to use to invoke a web
463 463 browser when opening the notebook. If not specified, the
464 464 default browser will be determined by the `webbrowser`
465 465 standard library module, which allows setting of the
466 466 BROWSER environment variable to override it.
467 467 """)
468 468
469 469 webapp_settings = Dict(config=True,
470 470 help="DEPRECATED, use tornado_settings"
471 471 )
472 472 def _webapp_settings_changed(self, name, old, new):
473 473 self.log.warn("\n webapp_settings is deprecated, use tornado_settings.\n")
474 474 self.tornado_settings = new
475 475
476 476 tornado_settings = Dict(config=True,
477 477 help="Supply overrides for the tornado.web.Application that the "
478 478 "IPython notebook uses.")
479 479
480 480 jinja_environment_options = Dict(config=True,
481 481 help="Supply extra arguments that will be passed to Jinja environment.")
482 482
483 483
484 484 enable_mathjax = Bool(True, config=True,
485 485 help="""Whether to enable MathJax for typesetting math/TeX
486 486
487 487 MathJax is the javascript library IPython uses to render math/LaTeX. It is
488 488 very large, so you may want to disable it if you have a slow internet
489 489 connection, or for offline use of the notebook.
490 490
491 491 When disabled, equations etc. will appear as their untransformed TeX source.
492 492 """
493 493 )
494 494 def _enable_mathjax_changed(self, name, old, new):
495 495 """set mathjax url to empty if mathjax is disabled"""
496 496 if not new:
497 497 self.mathjax_url = u''
498 498
499 499 base_url = Unicode('/', config=True,
500 500 help='''The base URL for the notebook server.
501 501
502 502 Leading and trailing slashes can be omitted,
503 503 and will automatically be added.
504 504 ''')
505 505 def _base_url_changed(self, name, old, new):
506 506 if not new.startswith('/'):
507 507 self.base_url = '/'+new
508 508 elif not new.endswith('/'):
509 509 self.base_url = new+'/'
510 510
511 511 base_project_url = Unicode('/', config=True, help="""DEPRECATED use base_url""")
512 512 def _base_project_url_changed(self, name, old, new):
513 513 self.log.warn("base_project_url is deprecated, use base_url")
514 514 self.base_url = new
515 515
516 516 extra_static_paths = List(Unicode, config=True,
517 517 help="""Extra paths to search for serving static files.
518 518
519 519 This allows adding javascript/css to be available from the notebook server machine,
520 520 or overriding individual files in the IPython"""
521 521 )
522 522 def _extra_static_paths_default(self):
523 523 return [os.path.join(self.profile_dir.location, 'static')]
524 524
525 525 @property
526 526 def static_file_path(self):
527 527 """return extra paths + the default location"""
528 528 return self.extra_static_paths + [DEFAULT_STATIC_FILES_PATH]
529 529
530 530 extra_template_paths = List(Unicode, config=True,
531 531 help="""Extra paths to search for serving jinja templates.
532 532
533 533 Can be used to override templates from IPython.html.templates."""
534 534 )
535 535 def _extra_template_paths_default(self):
536 536 return []
537 537
538 538 @property
539 539 def template_file_path(self):
540 540 """return extra paths + the default locations"""
541 541 return self.extra_template_paths + DEFAULT_TEMPLATE_PATH_LIST
542 542
543 543 nbextensions_path = List(Unicode, config=True,
544 544 help="""paths for Javascript extensions. By default, this is just IPYTHONDIR/nbextensions"""
545 545 )
546 546 def _nbextensions_path_default(self):
547 547 return [os.path.join(get_ipython_dir(), 'nbextensions')]
548 548
549 549 websocket_url = Unicode("", config=True,
550 550 help="""The base URL for websockets,
551 551 if it differs from the HTTP server (hint: it almost certainly doesn't).
552 552
553 553 Should be in the form of an HTTP origin: ws[s]://hostname[:port]
554 554 """
555 555 )
556 556 mathjax_url = Unicode("", config=True,
557 557 help="""The url for MathJax.js."""
558 558 )
559 559 def _mathjax_url_default(self):
560 560 if not self.enable_mathjax:
561 561 return u''
562 562 static_url_prefix = self.tornado_settings.get("static_url_prefix",
563 563 url_path_join(self.base_url, "static")
564 564 )
565 565
566 566 # try local mathjax, either in nbextensions/mathjax or static/mathjax
567 567 for (url_prefix, search_path) in [
568 568 (url_path_join(self.base_url, "nbextensions"), self.nbextensions_path),
569 569 (static_url_prefix, self.static_file_path),
570 570 ]:
571 571 self.log.debug("searching for local mathjax in %s", search_path)
572 572 try:
573 573 mathjax = filefind(os.path.join('mathjax', 'MathJax.js'), search_path)
574 574 except IOError:
575 575 continue
576 576 else:
577 577 url = url_path_join(url_prefix, u"mathjax/MathJax.js")
578 578 self.log.info("Serving local MathJax from %s at %s", mathjax, url)
579 579 return url
580 580
581 581 # no local mathjax, serve from CDN
582 582 url = u"https://cdn.mathjax.org/mathjax/latest/MathJax.js"
583 583 self.log.info("Using MathJax from CDN: %s", url)
584 584 return url
585 585
586 586 def _mathjax_url_changed(self, name, old, new):
587 587 if new and not self.enable_mathjax:
588 588 # enable_mathjax=False overrides mathjax_url
589 589 self.mathjax_url = u''
590 590 else:
591 591 self.log.info("Using MathJax: %s", new)
592 592
593 593 contents_manager_class = DottedObjectName('IPython.html.services.contents.filemanager.FileContentsManager',
594 594 config=True,
595 595 help='The notebook manager class to use.'
596 596 )
597 597 kernel_manager_class = DottedObjectName('IPython.html.services.kernels.kernelmanager.MappingKernelManager',
598 598 config=True,
599 599 help='The kernel manager class to use.'
600 600 )
601 601 session_manager_class = DottedObjectName('IPython.html.services.sessions.sessionmanager.SessionManager',
602 602 config=True,
603 603 help='The session manager class to use.'
604 604 )
605 605 cluster_manager_class = DottedObjectName('IPython.html.services.clusters.clustermanager.ClusterManager',
606 606 config=True,
607 607 help='The cluster manager class to use.'
608 608 )
609 609
610 610 kernel_spec_manager = Instance(KernelSpecManager)
611 611
612 612 def _kernel_spec_manager_default(self):
613 613 return KernelSpecManager(ipython_dir=self.ipython_dir)
614 614
615 615 trust_xheaders = Bool(False, config=True,
616 616 help=("Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headers"
617 617 "sent by the upstream reverse proxy. Necessary if the proxy handles SSL")
618 618 )
619 619
620 620 info_file = Unicode()
621 621
622 622 def _info_file_default(self):
623 623 info_file = "nbserver-%s.json"%os.getpid()
624 624 return os.path.join(self.profile_dir.security_dir, info_file)
625 625
626 626 pylab = Unicode('disabled', config=True,
627 627 help="""
628 628 DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib.
629 629 """
630 630 )
631 631 def _pylab_changed(self, name, old, new):
632 632 """when --pylab is specified, display a warning and exit"""
633 633 if new != 'warn':
634 634 backend = ' %s' % new
635 635 else:
636 636 backend = ''
637 637 self.log.error("Support for specifying --pylab on the command line has been removed.")
638 638 self.log.error(
639 639 "Please use `%pylab{0}` or `%matplotlib{0}` in the notebook itself.".format(backend)
640 640 )
641 641 self.exit(1)
642 642
643 643 notebook_dir = Unicode(config=True,
644 644 help="The directory to use for notebooks and kernels."
645 645 )
646 646
647 647 def _notebook_dir_default(self):
648 648 if self.file_to_run:
649 649 return os.path.dirname(os.path.abspath(self.file_to_run))
650 650 else:
651 651 return py3compat.getcwd()
652 652
653 653 def _notebook_dir_changed(self, name, old, new):
654 654 """Do a bit of validation of the notebook dir."""
655 655 if not os.path.isabs(new):
656 656 # If we receive a non-absolute path, make it absolute.
657 657 self.notebook_dir = os.path.abspath(new)
658 658 return
659 659 if not os.path.isdir(new):
660 660 raise TraitError("No such notebook dir: %r" % new)
661 661
662 662 # setting App.notebook_dir implies setting notebook and kernel dirs as well
663 663 self.config.FileContentsManager.root_dir = new
664 664 self.config.MappingKernelManager.root_dir = new
665 665
666 666
667 667 def parse_command_line(self, argv=None):
668 668 super(NotebookApp, self).parse_command_line(argv)
669 669
670 670 if self.extra_args:
671 671 arg0 = self.extra_args[0]
672 672 f = os.path.abspath(arg0)
673 673 self.argv.remove(arg0)
674 674 if not os.path.exists(f):
675 675 self.log.critical("No such file or directory: %s", f)
676 676 self.exit(1)
677 677
678 678 # Use config here, to ensure that it takes higher priority than
679 679 # anything that comes from the profile.
680 680 c = Config()
681 681 if os.path.isdir(f):
682 682 c.NotebookApp.notebook_dir = f
683 683 elif os.path.isfile(f):
684 684 c.NotebookApp.file_to_run = f
685 685 self.update_config(c)
686 686
687 687 def init_kernel_argv(self):
688 688 """add the profile-dir to arguments to be passed to IPython kernels"""
689 689 # FIXME: remove special treatment of IPython kernels
690 690 # Kernel should get *absolute* path to profile directory
691 691 self.ipython_kernel_argv = ["--profile-dir", self.profile_dir.location]
692 692
693 693 def init_configurables(self):
694 694 # force Session default to be secure
695 695 default_secure(self.config)
696 696 kls = import_item(self.kernel_manager_class)
697 697 self.kernel_manager = kls(
698 698 parent=self, log=self.log, ipython_kernel_argv=self.ipython_kernel_argv,
699 699 connection_dir = self.profile_dir.security_dir,
700 700 )
701 701 kls = import_item(self.contents_manager_class)
702 702 self.contents_manager = kls(parent=self, log=self.log)
703 703 kls = import_item(self.session_manager_class)
704 704 self.session_manager = kls(parent=self, log=self.log,
705 705 kernel_manager=self.kernel_manager,
706 706 contents_manager=self.contents_manager)
707 707 kls = import_item(self.cluster_manager_class)
708 708 self.cluster_manager = kls(parent=self, log=self.log)
709 709 self.cluster_manager.update_profiles()
710 710
711 711 def init_logging(self):
712 712 # This prevents double log messages because tornado use a root logger that
713 713 # self.log is a child of. The logging module dipatches log messages to a log
714 714 # and all of its ancenstors until propagate is set to False.
715 715 self.log.propagate = False
716 716
717 717 for log in app_log, access_log, gen_log:
718 718 # consistent log output name (NotebookApp instead of tornado.access, etc.)
719 719 log.name = self.log.name
720 720 # hook up tornado 3's loggers to our app handlers
721 721 logger = logging.getLogger('tornado')
722 722 logger.propagate = True
723 723 logger.parent = self.log
724 724 logger.setLevel(self.log.level)
725 725
726 726 def init_webapp(self):
727 727 """initialize tornado webapp and httpserver"""
728 728 self.tornado_settings['allow_origin'] = self.allow_origin
729 729 if self.allow_origin_pat:
730 730 self.tornado_settings['allow_origin_pat'] = re.compile(self.allow_origin_pat)
731 731 self.tornado_settings['allow_credentials'] = self.allow_credentials
732 732
733 733 self.web_app = NotebookWebApplication(
734 734 self, self.kernel_manager, self.contents_manager,
735 735 self.cluster_manager, self.session_manager, self.kernel_spec_manager,
736 736 self.log, self.base_url, self.default_url, self.tornado_settings,
737 737 self.jinja_environment_options
738 738 )
739 739 if self.certfile:
740 740 ssl_options = dict(certfile=self.certfile)
741 741 if self.keyfile:
742 742 ssl_options['keyfile'] = self.keyfile
743 743 else:
744 744 ssl_options = None
745 745 self.web_app.password = self.password
746 746 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options,
747 747 xheaders=self.trust_xheaders)
748 748 if not self.ip:
749 749 warning = "WARNING: The notebook server is listening on all IP addresses"
750 750 if ssl_options is None:
751 751 self.log.critical(warning + " and not using encryption. This "
752 752 "is not recommended.")
753 753 if not self.password:
754 754 self.log.critical(warning + " and not using authentication. "
755 755 "This is highly insecure and not recommended.")
756 756 success = None
757 757 for port in random_ports(self.port, self.port_retries+1):
758 758 try:
759 759 self.http_server.listen(port, self.ip)
760 760 except socket.error as e:
761 761 if e.errno == errno.EADDRINUSE:
762 762 self.log.info('The port %i is already in use, trying another random port.' % port)
763 763 continue
764 764 elif e.errno in (errno.EACCES, getattr(errno, 'WSAEACCES', errno.EACCES)):
765 765 self.log.warn("Permission to listen on port %i denied" % port)
766 766 continue
767 767 else:
768 768 raise
769 769 else:
770 770 self.port = port
771 771 success = True
772 772 break
773 773 if not success:
774 774 self.log.critical('ERROR: the notebook server could not be started because '
775 775 'no available port could be found.')
776 776 self.exit(1)
777 777
778 778 @property
779 779 def display_url(self):
780 780 ip = self.ip if self.ip else '[all ip addresses on your system]'
781 781 return self._url(ip)
782 782
783 783 @property
784 784 def connection_url(self):
785 785 ip = self.ip if self.ip else 'localhost'
786 786 return self._url(ip)
787 787
788 788 def _url(self, ip):
789 789 proto = 'https' if self.certfile else 'http'
790 790 return "%s://%s:%i%s" % (proto, ip, self.port, self.base_url)
791 791
792 792 def init_terminals(self):
793 793 try:
794 794 from .terminal import initialize
795 795 initialize(self.web_app)
796 796 self.web_app.settings['terminals_available'] = True
797 797 except ImportError as e:
798 798 self.log.info("Terminals not available (error was %s)", e)
799 799
800 800 def init_signal(self):
801 801 if not sys.platform.startswith('win'):
802 802 signal.signal(signal.SIGINT, self._handle_sigint)
803 803 signal.signal(signal.SIGTERM, self._signal_stop)
804 804 if hasattr(signal, 'SIGUSR1'):
805 805 # Windows doesn't support SIGUSR1
806 806 signal.signal(signal.SIGUSR1, self._signal_info)
807 807 if hasattr(signal, 'SIGINFO'):
808 808 # only on BSD-based systems
809 809 signal.signal(signal.SIGINFO, self._signal_info)
810 810
811 811 def _handle_sigint(self, sig, frame):
812 812 """SIGINT handler spawns confirmation dialog"""
813 813 # register more forceful signal handler for ^C^C case
814 814 signal.signal(signal.SIGINT, self._signal_stop)
815 815 # request confirmation dialog in bg thread, to avoid
816 816 # blocking the App
817 817 thread = threading.Thread(target=self._confirm_exit)
818 818 thread.daemon = True
819 819 thread.start()
820 820
821 821 def _restore_sigint_handler(self):
822 822 """callback for restoring original SIGINT handler"""
823 823 signal.signal(signal.SIGINT, self._handle_sigint)
824 824
825 825 def _confirm_exit(self):
826 826 """confirm shutdown on ^C
827 827
828 828 A second ^C, or answering 'y' within 5s will cause shutdown,
829 829 otherwise original SIGINT handler will be restored.
830 830
831 831 This doesn't work on Windows.
832 832 """
833 833 info = self.log.info
834 834 info('interrupted')
835 835 print(self.notebook_info())
836 836 sys.stdout.write("Shutdown this notebook server (y/[n])? ")
837 837 sys.stdout.flush()
838 838 r,w,x = select.select([sys.stdin], [], [], 5)
839 839 if r:
840 840 line = sys.stdin.readline()
841 841 if line.lower().startswith('y') and 'n' not in line.lower():
842 842 self.log.critical("Shutdown confirmed")
843 843 ioloop.IOLoop.instance().stop()
844 844 return
845 845 else:
846 846 print("No answer for 5s:", end=' ')
847 847 print("resuming operation...")
848 848 # no answer, or answer is no:
849 849 # set it back to original SIGINT handler
850 850 # use IOLoop.add_callback because signal.signal must be called
851 851 # from main thread
852 852 ioloop.IOLoop.instance().add_callback(self._restore_sigint_handler)
853 853
854 854 def _signal_stop(self, sig, frame):
855 855 self.log.critical("received signal %s, stopping", sig)
856 856 ioloop.IOLoop.instance().stop()
857 857
858 858 def _signal_info(self, sig, frame):
859 859 print(self.notebook_info())
860 860
861 861 def init_components(self):
862 862 """Check the components submodule, and warn if it's unclean"""
863 863 status = submodule.check_submodule_status()
864 864 if status == 'missing':
865 865 self.log.warn("components submodule missing, running `git submodule update`")
866 866 submodule.update_submodules(submodule.ipython_parent())
867 867 elif status == 'unclean':
868 868 self.log.warn("components submodule unclean, you may see 404s on static/components")
869 869 self.log.warn("run `setup.py submodule` or `git submodule update` to update")
870 870
871 871 @catch_config_error
872 872 def initialize(self, argv=None):
873 873 super(NotebookApp, self).initialize(argv)
874 874 self.init_logging()
875 875 self.init_kernel_argv()
876 876 self.init_configurables()
877 877 self.init_components()
878 878 self.init_webapp()
879 879 self.init_terminals()
880 880 self.init_signal()
881 881
882 882 def cleanup_kernels(self):
883 883 """Shutdown all kernels.
884 884
885 885 The kernels will shutdown themselves when this process no longer exists,
886 886 but explicit shutdown allows the KernelManagers to cleanup the connection files.
887 887 """
888 888 self.log.info('Shutting down kernels')
889 889 self.kernel_manager.shutdown_all()
890 890
891 891 def notebook_info(self):
892 892 "Return the current working directory and the server url information"
893 893 info = self.contents_manager.info_string() + "\n"
894 894 info += "%d active kernels \n" % len(self.kernel_manager._kernels)
895 895 return info + "The IPython Notebook is running at: %s" % self.display_url
896 896
897 897 def server_info(self):
898 898 """Return a JSONable dict of information about this server."""
899 899 return {'url': self.connection_url,
900 900 'hostname': self.ip if self.ip else 'localhost',
901 901 'port': self.port,
902 902 'secure': bool(self.certfile),
903 903 'base_url': self.base_url,
904 904 'notebook_dir': os.path.abspath(self.notebook_dir),
905 905 'pid': os.getpid()
906 906 }
907 907
908 908 def write_server_info_file(self):
909 909 """Write the result of server_info() to the JSON file info_file."""
910 910 with open(self.info_file, 'w') as f:
911 911 json.dump(self.server_info(), f, indent=2)
912 912
913 913 def remove_server_info_file(self):
914 914 """Remove the nbserver-<pid>.json file created for this server.
915 915
916 916 Ignores the error raised when the file has already been removed.
917 917 """
918 918 try:
919 919 os.unlink(self.info_file)
920 920 except OSError as e:
921 921 if e.errno != errno.ENOENT:
922 922 raise
923 923
924 924 def start(self):
925 925 """ Start the IPython Notebook server app, after initialization
926 926
927 927 This method takes no arguments so all configuration and initialization
928 928 must be done prior to calling this method."""
929 929 if self.subapp is not None:
930 930 return self.subapp.start()
931 931
932 932 info = self.log.info
933 933 for line in self.notebook_info().split("\n"):
934 934 info(line)
935 935 info("Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).")
936 936
937 937 self.write_server_info_file()
938 938
939 939 if self.open_browser or self.file_to_run:
940 940 try:
941 941 browser = webbrowser.get(self.browser or None)
942 942 except webbrowser.Error as e:
943 943 self.log.warn('No web browser found: %s.' % e)
944 944 browser = None
945 945
946 946 if self.file_to_run:
947 947 if not os.path.exists(self.file_to_run):
948 948 self.log.critical("%s does not exist" % self.file_to_run)
949 949 self.exit(1)
950 950
951 951 relpath = os.path.relpath(self.file_to_run, self.notebook_dir)
952 952 uri = url_path_join('notebooks', *relpath.split(os.sep))
953 953 else:
954 954 uri = 'tree'
955 955 if browser:
956 956 b = lambda : browser.open(url_path_join(self.connection_url, uri),
957 957 new=2)
958 958 threading.Thread(target=b).start()
959 959 try:
960 960 ioloop.IOLoop.instance().start()
961 961 except KeyboardInterrupt:
962 962 info("Interrupted...")
963 963 finally:
964 964 self.cleanup_kernels()
965 965 self.remove_server_info_file()
966 966
967 967
968 968 def list_running_servers(profile='default'):
969 969 """Iterate over the server info files of running notebook servers.
970 970
971 971 Given a profile name, find nbserver-* files in the security directory of
972 972 that profile, and yield dicts of their information, each one pertaining to
973 973 a currently running notebook server instance.
974 974 """
975 975 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), name=profile)
976 976 for file in os.listdir(pd.security_dir):
977 977 if file.startswith('nbserver-'):
978 978 with io.open(os.path.join(pd.security_dir, file), encoding='utf-8') as f:
979 979 info = json.load(f)
980 980
981 981 # Simple check whether that process is really still running
982 982 if check_pid(info['pid']):
983 983 yield info
984 984 else:
985 985 # If the process has died, try to delete its info file
986 986 try:
987 987 os.unlink(file)
988 988 except OSError:
989 989 pass # TODO: This should warn or log or something
990 990 #-----------------------------------------------------------------------------
991 991 # Main entry point
992 992 #-----------------------------------------------------------------------------
993 993
994 994 launch_new_instance = NotebookApp.launch_instance
995 995
1 NO CONTENT: file renamed from IPython/html/static/texteditor/js/editor.js to IPython/html/static/edit/js/editor.js
@@ -1,53 +1,53 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 require([
5 5 'base/js/namespace',
6 6 'base/js/utils',
7 7 'base/js/page',
8 8 'base/js/events',
9 9 'contents',
10 'texteditor/js/editor',
11 'texteditor/js/menubar',
12 'texteditor/js/notificationarea',
10 'edit/js/editor',
11 'edit/js/menubar',
12 'edit/js/notificationarea',
13 13 'custom/custom',
14 14 ], function(
15 15 IPython,
16 16 utils,
17 17 page,
18 18 events,
19 19 contents,
20 20 editor,
21 21 menubar,
22 22 notificationarea
23 23 ){
24 24 page = new page.Page();
25 25
26 26 var base_url = utils.get_body_data('baseUrl');
27 27 var file_path = utils.get_body_data('filePath');
28 28 contents = new contents.Contents({base_url: base_url});
29 29
30 30 var editor = new editor.Editor('#texteditor-container', {
31 31 base_url: base_url,
32 32 events: events,
33 33 contents: contents,
34 34 file_path: file_path,
35 35 });
36 36
37 37 // Make it available for debugging
38 38 IPython.editor = editor;
39 39
40 40 var menus = new menubar.MenuBar('#menubar', {
41 41 base_url: base_url,
42 42 editor: editor,
43 43 });
44 44
45 45 var notification_area = new notificationarea.EditorNotificationArea(
46 46 '#notification_area', {
47 47 events: events,
48 48 });
49 49 notification_area.init_notification_widgets();
50 50
51 51 editor.load();
52 52 page.show();
53 53 });
1 NO CONTENT: file renamed from IPython/html/static/texteditor/js/menubar.js to IPython/html/static/edit/js/menubar.js
1 NO CONTENT: file renamed from IPython/html/static/texteditor/js/notificationarea.js to IPython/html/static/edit/js/notificationarea.js
@@ -1,72 +1,72 b''
1 1 {% extends "page.html" %}
2 2
3 3 {% block title %}{{page_title}}{% endblock %}
4 4
5 5 {% block stylesheet %}
6 6 <link rel="stylesheet" href="{{ static_url('components/codemirror/lib/codemirror.css') }}">
7 7 <link rel="stylesheet" href="{{ static_url('components/codemirror/addon/dialog/dialog.css') }}">
8 8 <style>
9 9 #texteditor-container {
10 10 border-bottom: 1px solid #ccc;
11 11 }
12 12
13 13 #filename {
14 14 font-size: 16pt;
15 15 display: table;
16 16 padding: 0px 5px;
17 17 }
18 18 </style>
19 19
20 20 {{super()}}
21 21 {% endblock %}
22 22
23 23 {% block params %}
24 24
25 25 data-base-url="{{base_url}}"
26 26 data-file-path="{{file_path}}"
27 27
28 28 {% endblock %}
29 29
30 30 {% block header %}
31 31
32 32 <span id="filename">{{ basename }}</span>
33 33
34 34 {% endblock %}
35 35
36 36 {% block site %}
37 37
38 38 <div id="menubar-container" class="container">
39 39 <div id="menubar">
40 40 <div id="menus" class="navbar navbar-default" role="navigation">
41 41 <div class="container-fluid">
42 42 <button type="button" class="btn btn-default navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
43 43 <i class="fa fa-bars"></i>
44 44 <span class="navbar-text">Menu</span>
45 45 </button>
46 46 <ul class="nav navbar-nav navbar-right">
47 47 <li id="notification_area"></li>
48 48 </ul>
49 49 <div class="navbar-collapse collapse">
50 50 <ul class="nav navbar-nav">
51 51 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
52 52 <ul id="file_menu" class="dropdown-menu">
53 53 <li id="save_file"><a href="#">Save</a></li>
54 54 </ul>
55 55 </li>
56 56 </ul>
57 57 </div>
58 58 </div>
59 59 </div>
60 60 </div>
61 61 </div>
62 62
63 63 <div id="texteditor-container" class="container"></div>
64 64
65 65 {% endblock %}
66 66
67 67 {% block script %}
68 68
69 69 {{super()}}
70 70
71 <script src="{{ static_url("texteditor/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
71 <script src="{{ static_url("edit/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
72 72 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now