From dd76a232c1dc4b48491af566589cf527b4268ba5 2013-04-25 19:19:44 From: Brian E. Granger Date: 2013-04-25 19:19:44 Subject: [PATCH] Merge pull request #3088 from minrk/nbsettings cleanup IPython handler settings --- diff --git a/IPython/frontend/html/notebook/handlers.py b/IPython/frontend/html/notebook/handlers.py index 9b08859..7848c1d 100644 --- a/IPython/frontend/html/notebook/handlers.py +++ b/IPython/frontend/html/notebook/handlers.py @@ -32,9 +32,15 @@ from tornado.escape import url_escape from tornado import web from tornado import websocket +try: + from tornado.log import app_log +except ImportError: + app_log = logging.getLogger() + from zmq.eventloop import ioloop from zmq.utils import jsonapi +from IPython.config import Application from IPython.external.decorator import decorator from IPython.kernel.zmq.session import Session from IPython.lib.security import passwd_check @@ -96,14 +102,14 @@ if tornado.version_info <= (2,1,1): websocket.WebSocketHandler._execute = _execute del _execute - + #----------------------------------------------------------------------------- # Decorator for disabling read-only handlers #----------------------------------------------------------------------------- @decorator def not_if_readonly(f, self, *args, **kwargs): - if self.application.read_only: + if self.settings.get('read_only', False): raise web.HTTPError(403, "Notebook server is read-only") else: return f(self, *args, **kwargs) @@ -120,13 +126,13 @@ def authenticate_unless_readonly(f, self, *args, **kwargs): def auth_f(self, *args, **kwargs): return f(self, *args, **kwargs) - if self.application.read_only: + if self.settings.get('read_only', False): return f(self, *args, **kwargs) else: return auth_f(self, *args, **kwargs) def urljoin(*pieces): - """Join componenet of url into a relative url + """Join components of url into a relative url Use to prevent double slash when joining subpath """ @@ -147,19 +153,31 @@ class RequestHandler(web.RequestHandler): class AuthenticatedHandler(RequestHandler): """A RequestHandler with an authenticated user.""" + def clear_login_cookie(self): + self.clear_cookie(self.cookie_name) + def get_current_user(self): - user_id = self.get_secure_cookie(self.settings['cookie_name']) + user_id = self.get_secure_cookie(self.cookie_name) # For now the user_id should not return empty, but it could eventually if user_id == '': user_id = 'anonymous' if user_id is None: # prevent extra Invalid cookie sig warnings: - self.clear_cookie(self.settings['cookie_name']) - if not self.application.password and not self.application.read_only: + self.clear_login_cookie() + if not self.read_only and not self.login_available: user_id = 'anonymous' return user_id @property + def cookie_name(self): + return self.settings.get('cookie_name', '') + + @property + def password(self): + """our password""" + return self.settings.get('password', '') + + @property def logged_in(self): """Is a user currently logged in? @@ -175,20 +193,43 @@ class AuthenticatedHandler(RequestHandler): whether the user is already logged in or not. """ - return bool(self.application.password) + return bool(self.settings.get('password', '')) @property def read_only(self): """Is the notebook read-only? """ - return self.application.read_only + return self.settings.get('read_only', False) + +class IPythonHandler(AuthenticatedHandler): + """IPython-specific extensions to authenticated handling + + Mostly property shortcuts to IPython-specific settings. + """ + + @property + def config(self): + return self.settings.get('config', None) + + @property + def log(self): + """use the IPython log by default, falling back on tornado's logger""" + if Application.initialized(): + return Application.instance().log + else: + return app_log + @property def use_less(self): """Use less instead of css in templates""" - return self.application.use_less - + return self.settings.get('use_less', False) + + #--------------------------------------------------------------- + # URLs + #--------------------------------------------------------------- + @property def ws_url(self): """websocket url matching the current request @@ -197,13 +238,69 @@ class AuthenticatedHandler(RequestHandler): ws[s]://host[:port] """ proto = self.request.protocol.replace('http', 'ws') - host = self.application.ipython_app.websocket_host # default to config value + host = self.settings.get('websocket_host', '') + # default to config value if host == '': host = self.request.host # get from request return "%s://%s" % (proto, host) - + + @property + def mathjax_url(self): + return self.settings.get('mathjax_url', '') + + @property + def base_project_url(self): + return self.settings.get('base_project_url', '/') + + @property + def base_kernel_url(self): + return self.settings.get('base_kernel_url', '/') + + #--------------------------------------------------------------- + # Manager objects + #--------------------------------------------------------------- + + @property + def kernel_manager(self): + return self.settings['kernel_manager'] + + @property + def notebook_manager(self): + return self.settings['notebook_manager'] + + @property + def cluster_manager(self): + return self.settings['cluster_manager'] + + @property + def project(self): + return self.notebook_manager.notebook_dir + + #--------------------------------------------------------------- + # template rendering + #--------------------------------------------------------------- + + def get_template(self, name): + """Return the jinja template object for a given name""" + return self.settings['jinja2_env'].get_template(name) + + def render_template(self, name, **ns): + ns.update(self.template_namespace) + template = self.get_template(name) + return template.render(**ns) + + @property + def template_namespace(self): + return dict( + base_project_url=self.base_project_url, + base_kernel_url=self.base_kernel_url, + read_only=self.read_only, + logged_in=self.logged_in, + login_available=self.login_available, + use_less=self.use_less, + ) -class AuthenticatedFileHandler(AuthenticatedHandler, web.StaticFileHandler): +class AuthenticatedFileHandler(IPythonHandler, web.StaticFileHandler): """static files should only be accessible when logged in""" @authenticate_unless_readonly @@ -211,125 +308,89 @@ class AuthenticatedFileHandler(AuthenticatedHandler, web.StaticFileHandler): return web.StaticFileHandler.get(self, path) -class ProjectDashboardHandler(AuthenticatedHandler): +class ProjectDashboardHandler(IPythonHandler): @authenticate_unless_readonly def get(self): - nbm = self.application.notebook_manager - project = nbm.notebook_dir - template = self.application.jinja2_env.get_template('projectdashboard.html') - self.write( template.render( - project=project, - project_component=project.split('/'), - base_project_url=self.application.ipython_app.base_project_url, - base_kernel_url=self.application.ipython_app.base_kernel_url, - read_only=self.read_only, - logged_in=self.logged_in, - use_less=self.use_less, - login_available=self.login_available)) + self.write(self.render_template('projectdashboard.html', + project=self.project, + project_component=self.project.split('/'), + )) -class LoginHandler(AuthenticatedHandler): +class LoginHandler(IPythonHandler): - def _render(self, message=None): - template = self.application.jinja2_env.get_template('login.html') - self.write( template.render( - next=url_escape(self.get_argument('next', default=self.application.ipython_app.base_project_url)), - read_only=self.read_only, - logged_in=self.logged_in, - login_available=self.login_available, - base_project_url=self.application.ipython_app.base_project_url, - message=message + def _render(self, message=None): + self.write(self.render_template('login.html', + next=url_escape(self.get_argument('next', default=self.base_project_url)), + message=message, )) def get(self): if self.current_user: - self.redirect(self.get_argument('next', default=self.application.ipython_app.base_project_url)) + self.redirect(self.get_argument('next', default=self.base_project_url)) else: self._render() def post(self): pwd = self.get_argument('password', default=u'') - if self.application.password: - if passwd_check(self.application.password, pwd): - self.set_secure_cookie(self.settings['cookie_name'], str(uuid.uuid4())) + if self.login_available: + if passwd_check(self.password, pwd): + self.set_secure_cookie(self.cookie_name, str(uuid.uuid4())) else: self._render(message={'error': 'Invalid password'}) return - self.redirect(self.get_argument('next', default=self.application.ipython_app.base_project_url)) + self.redirect(self.get_argument('next', default=self.base_project_url)) -class LogoutHandler(AuthenticatedHandler): +class LogoutHandler(IPythonHandler): def get(self): - self.clear_cookie(self.settings['cookie_name']) + self.clear_login_cookie() if self.login_available: message = {'info': 'Successfully logged out.'} else: message = {'warning': 'Cannot log out. Notebook authentication ' 'is disabled.'} - template = self.application.jinja2_env.get_template('logout.html') - self.write( template.render( - read_only=self.read_only, - logged_in=self.logged_in, - login_available=self.login_available, - base_project_url=self.application.ipython_app.base_project_url, + self.write(self.render_template('logout.html', message=message)) -class NewHandler(AuthenticatedHandler): +class NewHandler(IPythonHandler): @web.authenticated def get(self): - nbm = self.application.notebook_manager - project = nbm.notebook_dir - notebook_id = nbm.new_notebook() - self.redirect('/'+urljoin(self.application.ipython_app.base_project_url, notebook_id)) + notebook_id = self.notebook_manager.new_notebook() + self.redirect('/' + urljoin(self.base_project_url, notebook_id)) -class NamedNotebookHandler(AuthenticatedHandler): +class NamedNotebookHandler(IPythonHandler): @authenticate_unless_readonly def get(self, notebook_id): - nbm = self.application.notebook_manager - project = nbm.notebook_dir + nbm = self.notebook_manager if not nbm.notebook_exists(notebook_id): raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id) - template = self.application.jinja2_env.get_template('notebook.html') - self.write( template.render( - project=project, + self.write(self.render_template('notebook.html', + project=self.project, notebook_id=notebook_id, - base_project_url=self.application.ipython_app.base_project_url, - base_kernel_url=self.application.ipython_app.base_kernel_url, kill_kernel=False, - read_only=self.read_only, - logged_in=self.logged_in, - login_available=self.login_available, - mathjax_url=self.application.ipython_app.mathjax_url, - use_less=self.use_less + mathjax_url=self.mathjax_url, ) ) -class PrintNotebookHandler(AuthenticatedHandler): +class PrintNotebookHandler(IPythonHandler): @authenticate_unless_readonly def get(self, notebook_id): - nbm = self.application.notebook_manager - project = nbm.notebook_dir - if not nbm.notebook_exists(notebook_id): + if not self.notebook_manager.notebook_exists(notebook_id): raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id) - template = self.application.jinja2_env.get_template('printnotebook.html') - self.write( template.render( - project=project, + self.write( self.render_template('printnotebook.html', + project=self.project, notebook_id=notebook_id, - base_project_url=self.application.ipython_app.base_project_url, - base_kernel_url=self.application.ipython_app.base_kernel_url, kill_kernel=False, - read_only=self.read_only, - logged_in=self.logged_in, - login_available=self.login_available, - mathjax_url=self.application.ipython_app.mathjax_url, + mathjax_url=self.mathjax_url, )) #----------------------------------------------------------------------------- @@ -337,17 +398,17 @@ class PrintNotebookHandler(AuthenticatedHandler): #----------------------------------------------------------------------------- -class MainKernelHandler(AuthenticatedHandler): +class MainKernelHandler(IPythonHandler): @web.authenticated def get(self): - km = self.application.kernel_manager + km = self.kernel_manager self.finish(jsonapi.dumps(km.list_kernel_ids())) @web.authenticated def post(self): - km = self.application.kernel_manager - nbm = self.application.notebook_manager + km = self.kernel_manager + nbm = self.notebook_manager notebook_id = self.get_argument('notebook', default=None) kernel_id = km.start_kernel(notebook_id, cwd=nbm.notebook_dir) data = {'ws_url':self.ws_url,'kernel_id':kernel_id} @@ -355,23 +416,23 @@ class MainKernelHandler(AuthenticatedHandler): self.finish(jsonapi.dumps(data)) -class KernelHandler(AuthenticatedHandler): +class KernelHandler(IPythonHandler): SUPPORTED_METHODS = ('DELETE') @web.authenticated def delete(self, kernel_id): - km = self.application.kernel_manager + km = self.kernel_manager km.shutdown_kernel(kernel_id) self.set_status(204) self.finish() -class KernelActionHandler(AuthenticatedHandler): +class KernelActionHandler(IPythonHandler): @web.authenticated def post(self, kernel_id, action): - km = self.application.kernel_manager + km = self.kernel_manager if action == 'interrupt': km.interrupt_kernel(kernel_id) self.set_status(204) @@ -384,6 +445,10 @@ class KernelActionHandler(AuthenticatedHandler): class ZMQStreamHandler(websocket.WebSocketHandler): + + def clear_cookie(self, *args, **kwargs): + """meaningless for websockets""" + pass def _reserialize_reply(self, msg_list): """Reserialize a reply message using JSON. @@ -413,7 +478,7 @@ class ZMQStreamHandler(websocket.WebSocketHandler): try: msg = self._reserialize_reply(msg_list) except Exception: - self.application.log.critical("Malformed message: %r" % msg_list, exc_info=True) + self.log.critical("Malformed message: %r" % msg_list, exc_info=True) else: self.write_message(msg) @@ -426,26 +491,14 @@ class ZMQStreamHandler(websocket.WebSocketHandler): return True -class AuthenticatedZMQStreamHandler(ZMQStreamHandler): +class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler): def open(self, kernel_id): self.kernel_id = kernel_id.decode('ascii') - try: - cfg = self.application.config - except AttributeError: - # protect from the case where this is run from something other than - # the notebook app: - cfg = None - self.session = Session(config=cfg) + self.session = Session(config=self.config) self.save_on_message = self.on_message self.on_message = self.on_first_message - def get_current_user(self): - user_id = self.get_secure_cookie(self.settings['cookie_name']) - if user_id == '' or (user_id is None and not self.application.password): - user_id = 'anonymous' - return user_id - def _inject_cookie_message(self, msg): """Inject the first message, which is the document cookie, for authentication.""" @@ -456,12 +509,12 @@ class AuthenticatedZMQStreamHandler(ZMQStreamHandler): try: self.request._cookies = Cookie.SimpleCookie(msg) except: - logging.warn("couldn't parse cookie string: %s",msg, exc_info=True) + self.log.warn("couldn't parse cookie string: %s",msg, exc_info=True) def on_first_message(self, msg): self._inject_cookie_message(msg) if self.get_current_user() is None: - logging.warn("Couldn't authenticate WebSocket connection") + self.log.warn("Couldn't authenticate WebSocket connection") raise web.HTTPError(403) self.on_message = self.save_on_message @@ -477,7 +530,7 @@ class IOPubHandler(AuthenticatedZMQStreamHandler): except web.HTTPError: self.close() return - km = self.application.kernel_manager + km = self.kernel_manager kernel_id = self.kernel_id km.add_restart_callback(kernel_id, self.on_kernel_restarted) km.add_restart_callback(kernel_id, self.on_restart_failed, 'dead') @@ -502,18 +555,18 @@ class IOPubHandler(AuthenticatedZMQStreamHandler): self.write_message(jsonapi.dumps(msg, default=date_default)) def on_kernel_restarted(self): - logging.warn("kernel %s restarted", self.kernel_id) + self.log.warn("kernel %s restarted", self.kernel_id) self._send_status_message('restarting') def on_restart_failed(self): - logging.error("kernel %s restarted failed!", self.kernel_id) + self.log.error("kernel %s restarted failed!", self.kernel_id) self._send_status_message('dead') def on_close(self): # This method can be called twice, once by self.kernel_died and once # from the WebSocket close event. If the WebSocket connection is # closed before the ZMQ streams are setup, they could be None. - km = self.application.kernel_manager + km = self.kernel_manager if self.kernel_id in km: km.remove_restart_callback( self.kernel_id, self.on_kernel_restarted, @@ -527,6 +580,10 @@ class IOPubHandler(AuthenticatedZMQStreamHandler): class ShellHandler(AuthenticatedZMQStreamHandler): + + @property + def max_msg_size(self): + return self.settings.get('max_msg_size', 65535) def initialize(self, *args, **kwargs): self.shell_stream = None @@ -537,8 +594,7 @@ class ShellHandler(AuthenticatedZMQStreamHandler): except web.HTTPError: self.close() return - km = self.application.kernel_manager - self.max_msg_size = km.max_msg_size + km = self.kernel_manager kernel_id = self.kernel_id try: self.shell_stream = km.connect_shell(kernel_id) @@ -566,26 +622,26 @@ class ShellHandler(AuthenticatedZMQStreamHandler): # Notebook web service handlers #----------------------------------------------------------------------------- -class NotebookRedirectHandler(AuthenticatedHandler): +class NotebookRedirectHandler(IPythonHandler): @authenticate_unless_readonly def get(self, notebook_name): - app = self.application # strip trailing .ipynb: notebook_name = os.path.splitext(notebook_name)[0] - notebook_id = app.notebook_manager.rev_mapping.get(notebook_name, '') + notebook_id = self.notebook_manager.rev_mapping.get(notebook_name, '') if notebook_id: url = self.settings.get('base_project_url', '/') + notebook_id return self.redirect(url) else: raise HTTPError(404) -class NotebookRootHandler(AuthenticatedHandler): + +class NotebookRootHandler(IPythonHandler): @authenticate_unless_readonly def get(self): - nbm = self.application.notebook_manager - km = self.application.kernel_manager + nbm = self.notebook_manager + km = self.kernel_manager files = nbm.list_notebooks() for f in files : f['kernel_id'] = km.kernel_for_notebook(f['notebook_id']) @@ -593,7 +649,7 @@ class NotebookRootHandler(AuthenticatedHandler): @web.authenticated def post(self): - nbm = self.application.notebook_manager + nbm = self.notebook_manager body = self.request.body.strip() format = self.get_argument('format', default='json') name = self.get_argument('name', default=None) @@ -605,13 +661,13 @@ class NotebookRootHandler(AuthenticatedHandler): self.finish(jsonapi.dumps(notebook_id)) -class NotebookHandler(AuthenticatedHandler): +class NotebookHandler(IPythonHandler): SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE') @authenticate_unless_readonly def get(self, notebook_id): - nbm = self.application.notebook_manager + nbm = self.notebook_manager format = self.get_argument('format', default='json') last_mod, name, data = nbm.get_notebook(notebook_id, format) @@ -626,7 +682,7 @@ class NotebookHandler(AuthenticatedHandler): @web.authenticated def put(self, notebook_id): - nbm = self.application.notebook_manager + nbm = self.notebook_manager format = self.get_argument('format', default='json') name = self.get_argument('name', default=None) nbm.save_notebook(notebook_id, self.request.body, name=name, format=format) @@ -635,20 +691,17 @@ class NotebookHandler(AuthenticatedHandler): @web.authenticated def delete(self, notebook_id): - nbm = self.application.notebook_manager - nbm.delete_notebook(notebook_id) + self.notebook_manager.delete_notebook(notebook_id) self.set_status(204) self.finish() -class NotebookCopyHandler(AuthenticatedHandler): +class NotebookCopyHandler(IPythonHandler): @web.authenticated def get(self, notebook_id): - nbm = self.application.notebook_manager - project = nbm.notebook_dir - notebook_id = nbm.copy_notebook(notebook_id) - self.redirect('/'+urljoin(self.application.ipython_app.base_project_url, notebook_id)) + notebook_id = self.notebook_manager.copy_notebook(notebook_id) + self.redirect('/'+urljoin(self.base_project_url, notebook_id)) #----------------------------------------------------------------------------- @@ -656,33 +709,31 @@ class NotebookCopyHandler(AuthenticatedHandler): #----------------------------------------------------------------------------- -class MainClusterHandler(AuthenticatedHandler): +class MainClusterHandler(IPythonHandler): @web.authenticated def get(self): - cm = self.application.cluster_manager - self.finish(jsonapi.dumps(cm.list_profiles())) + self.finish(jsonapi.dumps(self.cluster_manager.list_profiles())) -class ClusterProfileHandler(AuthenticatedHandler): +class ClusterProfileHandler(IPythonHandler): @web.authenticated def get(self, profile): - cm = self.application.cluster_manager - self.finish(jsonapi.dumps(cm.profile_info(profile))) + self.finish(jsonapi.dumps(self.cluster_manager.profile_info(profile))) -class ClusterActionHandler(AuthenticatedHandler): +class ClusterActionHandler(IPythonHandler): @web.authenticated def post(self, profile, action): - cm = self.application.cluster_manager + cm = self.cluster_manager if action == 'start': n = self.get_argument('n',default=None) if n is None: data = cm.start_cluster(profile) else: - data = cm.start_cluster(profile,int(n)) + data = cm.start_cluster(profile, int(n)) if action == 'stop': data = cm.stop_cluster(profile) self.finish(jsonapi.dumps(data)) @@ -693,7 +744,7 @@ class ClusterActionHandler(AuthenticatedHandler): #----------------------------------------------------------------------------- -class RSTHandler(AuthenticatedHandler): +class RSTHandler(IPythonHandler): @web.authenticated def post(self): @@ -841,7 +892,7 @@ class FileFindHandler(web.StaticFileHandler): try: abs_path = filefind(path, roots) except IOError: - logging.error("Could not find static file %r", path) + app_log.error("Could not find static file %r", path) return None # end subclass override @@ -854,7 +905,7 @@ class FileFindHandler(web.StaticFileHandler): hashes[abs_path] = hashlib.md5(f.read()).hexdigest() f.close() except Exception: - logging.error("Could not open static file %r", path) + app_log.error("Could not open static file %r", path) hashes[abs_path] = None hsh = hashes.get(abs_path) if hsh: diff --git a/IPython/frontend/html/notebook/kernelmanager.py b/IPython/frontend/html/notebook/kernelmanager.py index 7a5423d..12fccdd 100644 --- a/IPython/frontend/html/notebook/kernelmanager.py +++ b/IPython/frontend/html/notebook/kernelmanager.py @@ -29,18 +29,13 @@ from IPython.utils.traitlets import ( class MappingKernelManager(MultiKernelManager): - """A KernelManager that handles notebok mapping and HTTP error handling""" + """A KernelManager that handles notebook mapping and HTTP error handling""" def _kernel_manager_class_default(self): return "IPython.kernel.ioloop.IOLoopKernelManager" kernel_argv = List(Unicode) - max_msg_size = Integer(65536, config=True, help=""" - The max raw message size accepted from the browser - over a WebSocket connection. - """) - _notebook_mapping = Dict() #------------------------------------------------------------------------- diff --git a/IPython/frontend/html/notebook/notebookapp.py b/IPython/frontend/html/notebook/notebookapp.py index ff96f8c..c4035b4 100644 --- a/IPython/frontend/html/notebook/notebookapp.py +++ b/IPython/frontend/html/notebook/notebookapp.py @@ -178,16 +178,33 @@ class NotebookWebApplication(web.Application): # Note that the URLs these patterns check against are escaped, # and thus guaranteed to be ASCII: 'héllo' is really 'h%C3%A9llo'. base_project_url = py3compat.unicode_to_str(base_project_url, 'ascii') - + template_path = os.path.join(os.path.dirname(__file__), "templates") settings = dict( - template_path=os.path.join(os.path.dirname(__file__), "templates"), + # basics + base_project_url=base_project_url, + base_kernel_url=ipython_app.base_kernel_url, + template_path=template_path, static_path=ipython_app.static_file_path, static_handler_class = FileFindHandler, static_url_prefix = url_path_join(base_project_url,'/static/'), + + # authentication cookie_secret=os.urandom(1024), login_url=url_path_join(base_project_url,'/login'), cookie_name='username-%s' % uuid.uuid4(), - base_project_url = base_project_url, + read_only=ipython_app.read_only, + password=ipython_app.password, + + # managers + kernel_manager=kernel_manager, + notebook_manager=notebook_manager, + cluster_manager=cluster_manager, + + # IPython stuff + max_msg_size=ipython_app.max_msg_size, + config=ipython_app.config, + use_less=ipython_app.use_less, + jinja2_env=Environment(loader=FileSystemLoader(template_path)), ) # allow custom overrides for the tornado web app. @@ -197,21 +214,11 @@ class NotebookWebApplication(web.Application): new_handlers = [] for handler in handlers: pattern = url_path_join(base_project_url, handler[0]) - new_handler = tuple([pattern]+list(handler[1:])) - new_handlers.append( new_handler ) + new_handler = tuple([pattern] + list(handler[1:])) + new_handlers.append(new_handler) super(NotebookWebApplication, self).__init__(new_handlers, **settings) - self.kernel_manager = kernel_manager - self.notebook_manager = notebook_manager - self.cluster_manager = cluster_manager - self.ipython_app = ipython_app - self.read_only = self.ipython_app.read_only - self.config = self.ipython_app.config - self.use_less = self.ipython_app.use_less - self.log = log - self.jinja2_env = Environment(loader=FileSystemLoader(os.path.join(os.path.dirname(__file__), "templates"))) - #----------------------------------------------------------------------------- @@ -301,10 +308,17 @@ class NotebookApp(BaseIPythonApplication): kernel_argv = List(Unicode) - log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'), - default_value=logging.INFO, - config=True, - help="Set the log level by value or name.") + max_msg_size = Integer(65536, config=True, help=""" + The max raw message size accepted from the browser + over a WebSocket connection. + """) + + def _log_level_default(self): + return logging.INFO + + def _log_format_default(self): + """override default log format to include time""" + return u"%(asctime)s.%(msecs).03d [%(name)s] %(message)s" # create requested profiles by default, if they don't exist: auto_create = Bool(True) @@ -517,6 +531,14 @@ class NotebookApp(BaseIPythonApplication): # self.log is a child of. The logging module dipatches log messages to a log # and all of its ancenstors until propagate is set to False. self.log.propagate = False + + # set the date format + formatter = logging.Formatter(self.log_format, datefmt="%Y-%m-%d %H:%M:%S") + self.log.handlers[0].setFormatter(formatter) + + # hook up tornado 3's loggers to our app handlers + for name in ('access', 'application', 'general'): + logging.getLogger('tornado.%s' % name).handlers = self.log.handlers def init_webapp(self): """initialize tornado webapp and httpserver""" @@ -669,7 +691,7 @@ class NotebookApp(BaseIPythonApplication): return mgr_info +"The IPython Notebook is running at: %s" % self._url def start(self): - """ Start the IPython Notebok server app, after initialization + """ Start the IPython Notebook server app, after initialization This method takes no arguments so all configuration and initialization must be done prior to calling this method.""" diff --git a/IPython/frontend/html/notebook/zmqhttp.py b/IPython/frontend/html/notebook/zmqhttp.py deleted file mode 100644 index a439962..0000000 --- a/IPython/frontend/html/notebook/zmqhttp.py +++ /dev/null @@ -1,96 +0,0 @@ -"""Unfinished code for ZMQ/HTTP bridging. We use WebSockets instead. - -Authors: - -* Brian Granger -""" - -#----------------------------------------------------------------------------- -# Copyright (C) 2008-2011 The IPython Development Team -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#----------------------------------------------------------------------------- - -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- - -import json -import logging - -from tornado import web - -#----------------------------------------------------------------------------- -# Code -#----------------------------------------------------------------------------- - -class ZMQHandler(web.RequestHandler): - - def get_stream(self): - """Get the ZMQStream for this request.""" - raise NotImplementedError('Implement get_stream() in a subclass.') - - def _save_method_args(self, *args, **kwargs): - """Save the args and kwargs to get/post/put/delete for future use. - - These arguments are not saved in the request or handler objects, but - are often needed by methods such as get_stream(). - """ - self._method_args = args - self._method_kwargs = kwargs - - def _handle_msgs(self, msg): - msgs = [msg] - stream = self.get_stream() - stream.on_recv(lambda m: msgs.append(json.loads(m))) - stream.flush() - stream.stop_on_recv() - logging.info("Reply: %r" % msgs) - self.write(json.dumps(msgs)) - self.finish() - - -class ZMQPubHandler(ZMQHandler): - - SUPPORTED_METHODS = ("POST",) - - def post(self, *args, **kwargs): - self._save_method_args(*args, **kwargs) - try: - msg = json.loads(self.request.body) - except: - self.send_error(status_code=415) - else: - logging.info("Request: %r" % msg) - self.get_stream().send_json(msg) - - -class ZMQSubHandler(ZMQHandler): - - SUPPORTED_METHODS = ("GET",) - - @web.asynchronous - def get(self, *args, **kwargs): - self._save_method_args(*args, **kwargs) - self.get_stream().on_recv(self._handle_msgs) - - -class ZMQDealerHandler(ZMQHandler): - - SUPPORTED_METHODS = ("POST",) - - @web.asynchronous - def post(self, *args, **kwargs): - self._save_method_args(*args, **kwargs) - logging.info("request: %r" % self.request) - try: - msg = json.loads(self.request.body) - except: - self.send_error(status_code=415) - else: - logging.info("Reply: %r" % msg) - stream = self.get_stream() - stream.send_json(msg) - stream.on_recv(self._handle_msgs) -