3636template_file = os .path .join (conf_path , 'cert.cfg' )
3737
3838FLASK_NOTEBOOK_CONFIG = """
39+ ####################################################################
40+ # WARNING -- Do not edit this file! It is autogenerated each time
41+ # the notebook(...) command is executed.
42+ ####################################################################
43+
44+ import sagenb.notebook.misc
45+ sagenb.notebook.misc.DIR = %(cwd)r #We should really get rid of this!
46+
47+ import signal, sys, random
48+ def save_notebook(notebook):
49+ print "Quitting all running worksheets..."
50+ notebook.quit()
51+ print "Saving notebook..."
52+ notebook.save()
53+ print "Notebook cleanly saved."
54+
55+ #########
56+ # Flask #
57+ #########
58+ import os, sys, random
59+ flask_dir = os.path.join(os.environ['SAGE_ROOT'], 'devel', 'sagenb', 'flask_version')
60+ sys.path.append(flask_dir)
61+ import base as flask_base
62+ opts={}
63+ startup_token = '{0:x}'.format(random.randint(0, 2**128))
64+ if %(login)s:
65+ opts['startup_token'] = startup_token
66+ flask_app = flask_base.create_app(%(notebook_opts)s, **opts)
67+ sys.path.remove(flask_dir)
68+
69+ if %(secure)s:
70+ from OpenSSL import SSL
71+ ssl_context = SSL.Context(SSL.SSLv23_METHOD)
72+ ssl_context.use_privatekey_file(%(pkey)r)
73+ ssl_context.use_certificate_file(%(cert)r)
74+ else:
75+ ssl_context = None
76+
77+ import logging
78+ logger=logging.getLogger('werkzeug')
79+ #logger.setLevel(logging.WARNING)
80+ #logger.setLevel(logging.INFO) # to see page requests
81+ logger.setLevel(logging.DEBUG)
82+ logger.addHandler(logging.StreamHandler())
83+
84+ if %(secure)s:
85+ # Monkey-patch werkzeug so that it works with pyOpenSSL and Python 2.7
86+ # otherwise, we constantly get TypeError: shutdown() takes exactly 0 arguments (1 given)
87+
88+ # Monkey patching idiom: http://mail.python.org/pipermail/python-dev/2008-January/076194.html
89+ def monkeypatch_method(cls):
90+ def decorator(func):
91+ setattr(cls, func.__name__, func)
92+ return func
93+ return decorator
94+ from werkzeug import serving
95+
96+ @monkeypatch_method(serving.BaseWSGIServer)
97+ def shutdown_request(self, request):
98+ request.shutdown()
99+
100+ %(open_page)s
101+ try:
102+ flask_app.run(host=%(host)r, port=%(port)s, threaded=True,
103+ ssl_context=ssl_context, debug=False)
104+ finally:
105+ save_notebook(flask_base.notebook)
106+ """
107+
108+ TWISTD_NOTEBOOK_CONFIG = """
39109####################################################################
40110# WARNING -- Do not edit this file! It is autogenerated each time
41111# the notebook(...) command is executed.
50120
51121from twisted.internet import reactor
52122
53- # Now set things up and start the notebook
54- import sagenb.notebook.notebook
55- sagenb.notebook.notebook.JSMATH=True
56- import sagenb.notebook.notebook as notebook
57- import sagenb.notebook.worksheet as worksheet
58-
59- import sagenb.notebook.misc as misc
60-
61- misc.DIR = %(cwd)r #We should really get rid of this!
123+ import sagenb.notebook.misc
124+ sagenb.notebook.misc.DIR = %(cwd)r #We should really get rid of this!
62125
63- import signal, sys, random
126+ import signal
64127def save_notebook(notebook):
65128 from twisted.internet.error import ReactorNotRunning
66129 print "Quitting all running worksheets..."
@@ -84,7 +147,7 @@ def my_sigint(x, n):
84147#########
85148# Flask #
86149#########
87- import os
150+ import os, sys, random
88151flask_dir = os.path.join(os.environ['SAGE_ROOT'], 'devel', 'sagenb', 'flask_version')
89152sys.path.append(flask_dir)
90153import base as flask_base
@@ -215,16 +278,16 @@ def notebook_setup(self=None):
215278 '--outfile %s' % (template_file , private_pem , public_pem )]
216279 print cmd [0 ]
217280 subprocess .call (cmd , shell = True )
218-
281+
219282 # Set permissions on private cert
220283 os .chmod (private_pem , 0600 )
221284
222285 print "Successfully configured notebook."
223286
224- def notebook_twisted (self ,
287+ def notebook_run (self ,
225288 directory = None ,
226289 port = 8080 ,
227- interface = 'localhost' ,
290+ interface = 'localhost' ,
228291 port_tries = 50 ,
229292 secure = False ,
230293 reset = False ,
@@ -241,7 +304,10 @@ def notebook_twisted(self,
241304 start_path = "" ,
242305 fork = False ,
243306 quiet = False ,
244-
307+
308+ server = "twistd" ,
309+ profile = False ,
310+
245311 subnets = None ,
246312 require_login = None ,
247313 open_viewer = None ,
@@ -285,10 +351,9 @@ def notebook_twisted(self,
285351 # if none use defaults
286352
287353 nb = notebook .load_notebook (directory )
288-
354+
289355 directory = nb ._dir
290- conf = os .path .join (directory , 'twistedconf.tac' )
291-
356+
292357 if not quiet :
293358 print "The notebook files are stored in:" , nb ._dir
294359
@@ -314,7 +379,7 @@ def notebook_twisted(self,
314379
315380 if not nb .user_manager ().user_exists ('admin' ):
316381 reset = True
317-
382+
318383 if reset :
319384 passwd = get_admin_passwd ()
320385 if reset :
@@ -345,9 +410,52 @@ def notebook_twisted(self,
345410 nb .save ()
346411 del nb
347412
348- def run (port ):
413+ def run_flask ():
414+ """Run a flask (werkzeug) webserver."""
415+ # TODO: Check to see if server is running already (PID file?)
416+ conf = os .path .join (directory , 'run_flask' )
417+
418+ notebook_opts = '"%s",interface="%s",port=%s,secure=%s' % (
419+ os .path .abspath (directory ), interface , port , secure )
420+
421+ if automatic_login :
422+ start_path = "'/?startup_token=%s' % startup_token"
423+ if interface :
424+ hostname = interface
425+ else :
426+ hostname = 'localhost'
427+ open_page = "from sagenb.misc.misc import open_page; open_page('%s', %s, %s, %s)" % (hostname , port , secure , start_path )
428+ else :
429+ open_page = ''
430+
431+ config = open (conf , 'w' )
432+
433+ config .write (FLASK_NOTEBOOK_CONFIG % {'notebook_opts' : notebook_opts ,
434+ 'cwd' :cwd ,
435+ 'open_page' : open_page , 'login' : automatic_login ,
436+ 'secure' : secure , 'pkey' : private_pem , 'cert' : public_pem ,
437+ 'host' : interface , 'port' : port })
438+
439+ config .close ()
440+
441+ if profile :
442+ import random
443+ if isinstance (profile , basestring ):
444+ profilefile = profile + '%s.stats' % random .random ()
445+ else :
446+ profilefile = 'sagenb-flask-profile-%s.stats' % random .random ()
447+ profilecmd = '-m cProfile -o %s' % profilefile
448+ else :
449+ profilecmd = ''
450+ cmd = 'python %s %s' % (profilecmd , conf )
451+ return cmd
452+ # end of inner function run_flask
453+
454+ def run_twistd ():
455+ """Run a twistd webserver."""
349456 # Is a server already running? Check if a Twistd PID exists in
350457 # the given directory.
458+ conf = os .path .join (directory , 'twistedconf.tac' )
351459 pidfile = os .path .join (directory , 'twistd.pid' )
352460 if platformType != 'win32' :
353461 from twisted .scripts ._twistd_unix import checkPID
@@ -358,6 +466,7 @@ def run(port):
358466
359467 if str (e ).startswith ('Another twistd server is running,' ):
360468 print 'Another Sage Notebook server is running, PID %d.' % pid
469+
361470 old_interface , old_port , old_secure = get_old_settings (conf )
362471 if automatic_login and old_port :
363472 old_interface = old_interface or 'localhost'
@@ -367,20 +476,12 @@ def run(port):
367476
368477 from sagenb .misc .misc import open_page as browse_to
369478 browse_to (old_interface , old_port , old_secure , '/' )
370- return
479+ return None
371480 print '\n Please either stop the old server or run the new server in a different directory.'
372- return
481+ return None
373482
374483 ## Create the config file
375484 if secure :
376- if (not os .path .exists (private_pem ) or
377- not os .path .exists (public_pem )):
378- print "In order to use an SECURE encrypted notebook, you must first run notebook.setup()."
379- print "Now running notebook.setup()"
380- notebook_setup ()
381- if (not os .path .exists (private_pem ) or
382- not os .path .exists (public_pem )):
383- print "Failed to setup notebook. Please try notebook.setup() again manually."
384485 strport = '%s:%s:interface=%s:privateKey=%s:certKey=%s' % (
385486 protocol , port , interface , private_pem , public_pem )
386487 else :
@@ -401,35 +502,27 @@ def run(port):
401502
402503 config = open (conf , 'w' )
403504
404- config .write (FLASK_NOTEBOOK_CONFIG % {'notebook_opts' : notebook_opts ,
505+ config .write (TWISTD_NOTEBOOK_CONFIG % {'notebook_opts' : notebook_opts ,
405506 'cwd' :cwd , 'strport' : strport ,
406507 'open_page' : open_page , 'login' : automatic_login })
407508
408509
409- config .close ()
510+ config .close ()
410511
411512 ## Start up twisted
412- cmd = 'twistd --pidfile="%s" -ny "%s"' % (pidfile , conf )
413- if not quiet :
414- print_open_msg ('localhost' if not interface else interface ,
415- port , secure = secure )
416- if secure and not quiet :
417- print "There is an admin account. If you do not remember the password,"
418- print "quit the notebook and type notebook(reset=True)."
419-
420- if fork :
421- import pexpect
422- return pexpect .spawn (cmd )
513+ if profile :
514+ import random
515+ if isinstance (profile , basestring ):
516+ profilefile = profile + '%s.stats' % random .random ()
517+ else :
518+ profilefile = 'sagenb-twistd-profile-%s.stats' % random .random ()
519+ profilecmd = '--profile=%s --profiler=cprofile --savestats' % profilefile
423520 else :
424- e = os .system (cmd )
425-
426- os .chdir (cwd )
427- if e == 256 :
428- raise socket .error
521+ profilecmd = ''
522+ cmd = 'twistd %s --pidfile="%s" -ny "%s"' % (profilecmd , pidfile , conf )
523+ return cmd
524+ # end of inner function run_twistd
429525
430- return True
431- # end of inner function run
432-
433526 if interface != 'localhost' and not secure :
434527 print "*" * 70
435528 print "WARNING: Insecure notebook server listening on external interface."
@@ -440,7 +533,38 @@ def run(port):
440533 port = find_next_available_port (interface , port , port_tries )
441534 if automatic_login :
442535 "Automatic login isn't fully implemented. You have to manually open your web browser to the above URL."
443- return run (port )
536+ if secure :
537+ if (not os .path .exists (private_pem ) or
538+ not os .path .exists (public_pem )):
539+ print "In order to use an SECURE encrypted notebook, you must first run notebook.setup()."
540+ print "Now running notebook.setup()"
541+ notebook_setup ()
542+ if (not os .path .exists (private_pem ) or
543+ not os .path .exists (public_pem )):
544+ print "Failed to setup notebook. Please try notebook.setup() again manually."
545+ if server == "flask" :
546+ cmd = run_flask ()
547+ elif server == "twistd" :
548+ cmd = run_twistd ()
549+ if cmd is None :
550+ return
551+
552+ if not quiet :
553+ print_open_msg ('localhost' if not interface else interface ,
554+ port , secure = secure )
555+ if secure and not quiet :
556+ print "There is an admin account. If you do not remember the password,"
557+ print "quit the notebook and type notebook(reset=True)."
558+ print "Executing" , cmd
559+ if fork :
560+ import pexpect
561+ return pexpect .spawn (cmd )
562+ else :
563+ e = os .system (cmd )
564+
565+ os .chdir (cwd )
566+ if e == 256 :
567+ raise socket .error
444568
445569def get_admin_passwd ():
446570 print "\n " * 2
0 commit comments