Skip to content

Commit

Permalink
cleanup pass
Browse files Browse the repository at this point in the history
- logging
- process cleanup
- docstrings
- COPYING.md
  • Loading branch information
minrk committed Aug 20, 2014
1 parent f39256f commit 0f4537e
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 134 deletions.
59 changes: 59 additions & 0 deletions COPYING.md
@@ -0,0 +1,59 @@
# The Jupyter multi-user notebook server licensing terms

Jupyter multi-user notebook server is licensed under the terms of the Modified BSD License
(also known as New or Revised or 3-Clause BSD), as follows:

- Copyright (c) 2014-, Jupyter Development Team

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

Neither the name of the Jupyter Development Team nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

## About the Jupyter Development Team

The Jupyter Development Team is the set of all contributors to the Jupyter project.
This includes all of the Jupyter subprojects.

The core team that coordinates development on GitHub can be found here:
https://github.com/jupyter/.

## Our Copyright Policy

Jupyter uses a shared copyright model. Each contributor maintains copyright
over their contributions to Jupyter. But, it is important to note that these
contributions are typically only changes to the repositories. Thus, the Jupyter
source code, in its entirety is not the copyright of any single person or
institution. Instead, it is the collective copyright of the entire Jupyter
Development Team. If individual contributors want to maintain a record of what
changes/contributions they have specific copyright on, they should indicate
their copyright in the commit message of the change, when they commit the
change to one of the Jupyter repositories.

With this in mind, the following banner should be used in any source code file
to indicate the copyright and license terms:

# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
12 changes: 6 additions & 6 deletions README.md
Expand Up @@ -4,16 +4,16 @@ This repo hosts the development of a multi-user server to manage and proxy multi

Three actors:

- multi-user server (tornado process)
- multi-user Hub (tornado process)
- configurable http proxy (node-http-proxy)
- multiple single-user IPython notbeook servers (Python/IPython/tornado)
- multiple single-user IPython notebook servers (Python/IPython/tornado)

Basic principals:

- MUS spawns proxy
- proxy forwards ~all requests to MUS by default
- MUS handles login, and spawns single-user servers on demand
- MUS configures proxy to forward url prefixes to single-user servers
- Hub spawns proxy
- Proxy forwards ~all requests to hub by default
- Hub handles login, and spawns single-user servers on demand
- Hub configures proxy to forward url prefixes to single-user servers

## dependencies

Expand Down
80 changes: 62 additions & 18 deletions multiuser/app.py
@@ -1,19 +1,21 @@
#!/usr/bin/env python
"""The multi-user notebook application"""

import logging
import os
from subprocess import Popen

import tornado.httpserver
import tornado.ioloop
import tornado.options
from tornado.log import LogFormatter
from tornado import web

from IPython.utils.traitlets import (
Unicode, Integer, Dict, TraitError, List, Instance, Bool, Bytes, Any,
DottedObjectName,
)
from IPython.config import Application
from IPython.html.utils import url_path_join
from IPython.utils.importstring import import_item

here = os.path.dirname(__file__)
Expand All @@ -27,11 +29,10 @@
)

from . import db

# from .user import UserManager
from .utils import url_path_join

class MultiUserApp(Application):

"""An Application for starting the Multi-User Notebook server."""
ip = Unicode('localhost', config=True,
help="The public facing ip of the proxy"
)
Expand Down Expand Up @@ -86,15 +87,8 @@ def _hub_prefix_changed(self, name, old, new):
def _cookie_secret_default(self):
return b'secret!'

# spawning subprocesses
spawner_class = DottedObjectName("multiuser.spawner.ProcessSpawner")
def _spawner_class_changed(self, name, old, new):
self.spawner = import_item(new)

spawner = Any()
def _spawner_default(self):
return import_item(self.spawner_class)

# class for spawning single-user servers
spawner_class = DottedObjectName("multiuser.spawner.LocalProcessSpawner")

db_url = Unicode('sqlite:///:memory:', config=True)
debug_db = Bool(False)
Expand All @@ -104,7 +98,35 @@ def _spawner_default(self):

handlers = List()

def add_url_prefix(self, prefix, handlers):

_log_formatter_cls = LogFormatter

def _log_level_default(self):
return logging.INFO

def _log_datefmt_default(self):
"""Exclude date from default date format"""
return "%H:%M:%S"

def _log_format_default(self):
"""override default log format to include time"""
return u"%(color)s[%(levelname)1.1s %(asctime)s.%(msecs).03d %(name)s]%(end_color)s %(message)s"

def init_logging(self):
# This prevents double log messages because tornado use a root logger that
# 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

# hook up tornado 3's loggers to our app handlers
logger = logging.getLogger('tornado')
logger.propagate = True
logger.parent = self.log
logger.setLevel(self.log.level)


@staticmethod
def add_url_prefix(prefix, handlers):
"""add a url prefix to handlers"""
for i, tup in enumerate(handlers):
lis = list(tup)
Expand All @@ -127,9 +149,11 @@ def init_handlers(self):

def init_db(self):
# TODO: load state from db for resume
# TODO: if not resuming, clear existing db contents
self.db = db.new_session(self.db_url, echo=self.debug_db)

def init_hub(self):
"""Load the Hub config into the database"""
self.hub = db.Hub(
server=db.Server(
ip=self.hub_ip,
Expand All @@ -143,6 +167,7 @@ def init_hub(self):
self.db.commit()

def init_proxy(self):
"""Load the Proxy config into the database"""
self.proxy = db.Proxy(
public_server=db.Server(
ip=self.ip,
Expand All @@ -159,6 +184,7 @@ def init_proxy(self):
self.db.commit()

def start_proxy(self):
"""Actually start the configurable-http-proxy"""
env = os.environ.copy()
env['CONFIGPROXY_AUTH_TOKEN'] = self.proxy.auth_token
self.proxy = Popen(["node", os.path.join(here, 'js', 'main.js'),
Expand All @@ -168,17 +194,24 @@ def start_proxy(self):
], env=env)

def init_tornado_settings(self):
"""Set up the tornado settings dict."""
base_url = self.base_url
self.tornado_settings.update(
settings = dict(
config=self.config,
db=self.db,
hub=self.hub,
spawner_class=import_item(self.spawner_class),
base_url=base_url,
cookie_secret=self.cookie_secret,
login_url=url_path_join(self.hub.server.base_url, 'login'),
template_path=os.path.join(here, 'templates'),
)
# allow configured settings to have priority
settings.update(self.tornado_settings)
self.tornado_settings = settings

def init_tornado_application(self):
"""Instantiate the tornado Application object"""
self.tornado_application = web.Application(self.handlers, **self.tornado_settings)

def initialize(self, *args, **kwargs):
Expand All @@ -190,18 +223,29 @@ def initialize(self, *args, **kwargs):
self.init_tornado_settings()
self.init_tornado_application()

def cleanup(self):
self.log.info("Cleaning up proxy...")
self.proxy.terminate()
self.log.info("Cleaning up single-user servers...")
Spawner = import_item(self.spawner_class)
for user in self.db.query(db.User):
if user.spawner is not None:
user.spawner.stop()
self.log.info("...done")

def start(self):
"""Start the whole thing"""
# start the proxy
self.start_proxy()
# start the webserver
http_server = tornado.httpserver.HTTPServer(self.tornado_application)
http_server.listen(self.hub_port)
try:
tornado.ioloop.IOLoop.instance().start()
except KeyboardInterrupt:
print("\nInterrupted")
finally:
pass
# self.proxy.terminate()
# self.user_manager.cleanup()
self.cleanup()

main = MultiUserApp.launch_instance

Expand Down

0 comments on commit 0f4537e

Please sign in to comment.