use traitlets-4.1 observe/default decorators

This commit is contained in:
Min RK
2016-03-27 10:41:36 -07:00
parent fa48620076
commit 7a2794af7c
6 changed files with 56 additions and 19 deletions

View File

@@ -36,6 +36,7 @@ from tornado import gen, web
from traitlets import ( from traitlets import (
Unicode, Integer, Dict, TraitError, List, Bool, Any, Unicode, Integer, Dict, TraitError, List, Bool, Any,
Type, Set, Instance, Bytes, Float, Type, Set, Instance, Bytes, Float,
observe, default,
) )
from traitlets.config import Application, catch_config_error from traitlets.config import Application, catch_config_error
@@ -209,6 +210,7 @@ class JupyterHub(Application):
help="Paths to search for jinja templates.", help="Paths to search for jinja templates.",
).tag(config=True) ).tag(config=True)
@default('template_paths')
def _template_paths_default(self): def _template_paths_default(self):
return [os.path.join(self.data_files_path, 'templates')] return [os.path.join(self.data_files_path, 'templates')]
@@ -262,6 +264,8 @@ class JupyterHub(Application):
logo_file = Unicode('', logo_file = Unicode('',
help="Specify path to a logo image to override the Jupyter logo in the banner." help="Specify path to a logo image to override the Jupyter logo in the banner."
).tag(config=True) ).tag(config=True)
@default('logo_file')
def _logo_file_default(self): def _logo_file_default(self):
return os.path.join(self.data_files_path, 'static', 'images', 'jupyter.png') return os.path.join(self.data_files_path, 'static', 'images', 'jupyter.png')
@@ -284,6 +288,8 @@ class JupyterHub(Application):
Loaded from the CONFIGPROXY_AUTH_TOKEN env variable by default. Loaded from the CONFIGPROXY_AUTH_TOKEN env variable by default.
""" """
).tag(config=True) ).tag(config=True)
@default('proxy_auth_token')
def _proxy_auth_token_default(self): def _proxy_auth_token_default(self):
token = os.environ.get('CONFIGPROXY_AUTH_TOKEN', None) token = os.environ.get('CONFIGPROXY_AUTH_TOKEN', None)
if not token: if not token:
@@ -302,6 +308,8 @@ class JupyterHub(Application):
proxy_api_port = Integer( proxy_api_port = Integer(
help="The port for the proxy API handlers" help="The port for the proxy API handlers"
).tag(config=True) ).tag(config=True)
@default('proxy_api_port')
def _proxy_api_port_default(self): def _proxy_api_port_default(self):
return self.port + 1 return self.port + 1
@@ -314,21 +322,27 @@ class JupyterHub(Application):
hub_prefix = URLPrefix('/hub/', hub_prefix = URLPrefix('/hub/',
help="The prefix for the hub server. Must not be '/'" help="The prefix for the hub server. Must not be '/'"
).tag(config=True) ).tag(config=True)
@default('hub_prefix')
def _hub_prefix_default(self): def _hub_prefix_default(self):
return url_path_join(self.base_url, '/hub/') return url_path_join(self.base_url, '/hub/')
@observe('hub_prefix')
def _hub_prefix_changed(self, name, old, new): def _hub_prefix_changed(self, name, old, new):
if new == '/': if new == '/':
raise TraitError("'/' is not a valid hub prefix") raise TraitError("'/' is not a valid hub prefix")
if not new.startswith(self.base_url): if not new.startswith(self.base_url):
self.hub_prefix = url_path_join(self.base_url, new) self.hub_prefix = url_path_join(self.base_url, new)
cookie_secret = Bytes(env='JPY_COOKIE_SECRET', cookie_secret = Bytes(
help="""The cookie secret to use to encrypt cookies. help="""The cookie secret to use to encrypt cookies.
Loaded from the JPY_COOKIE_SECRET env variable by default. Loaded from the JPY_COOKIE_SECRET env variable by default.
""" """
).tag(config=True) ).tag(
config=True,
env='JPY_COOKIE_SECRET',
)
cookie_secret_file = Unicode('jupyterhub_cookie_secret', cookie_secret_file = Unicode('jupyterhub_cookie_secret',
help="""File in which to store the cookie secret.""" help="""File in which to store the cookie secret."""
@@ -350,6 +364,8 @@ class JupyterHub(Application):
).tag(config=True) ).tag(config=True)
authenticator = Instance(Authenticator) authenticator = Instance(Authenticator)
@default('authenticator')
def _authenticator_default(self): def _authenticator_default(self):
return self.authenticator_class(parent=self, db=self.db) return self.authenticator_class(parent=self, db=self.db)
@@ -364,7 +380,10 @@ class JupyterHub(Application):
db_url = Unicode('sqlite:///jupyterhub.sqlite', db_url = Unicode('sqlite:///jupyterhub.sqlite',
help="url for the database. e.g. `sqlite:///jupyterhub.sqlite`" help="url for the database. e.g. `sqlite:///jupyterhub.sqlite`"
).tag(config=True) ).tag(config=True)
def _db_url_changed(self, name, old, new):
@observe('db_url')
def _db_url_changed(self, change):
new = change['new']
if '://' not in new: if '://' not in new:
# assume sqlite, if given as a plain filename # assume sqlite, if given as a plain filename
self.db_url = 'sqlite:///%s' % new self.db_url = 'sqlite:///%s' % new
@@ -384,6 +403,8 @@ class JupyterHub(Application):
session_factory = Any() session_factory = Any()
users = Instance(UserDict) users = Instance(UserDict)
@default('users')
def _users_default(self): def _users_default(self):
assert self.tornado_settings assert self.tornado_settings
return UserDict(db_factory=lambda : self.db, settings=self.tornado_settings) return UserDict(db_factory=lambda : self.db, settings=self.tornado_settings)
@@ -435,13 +456,16 @@ class JupyterHub(Application):
proxy_process = None proxy_process = None
io_loop = None io_loop = None
@default('log_level')
def _log_level_default(self): def _log_level_default(self):
return logging.INFO return logging.INFO
@default('log_datefmt')
def _log_datefmt_default(self): def _log_datefmt_default(self):
"""Exclude date from default date format""" """Exclude date from default date format"""
return "%Y-%m-%d %H:%M:%S" return "%Y-%m-%d %H:%M:%S"
@default('log_format')
def _log_format_default(self): def _log_format_default(self):
"""override default log format to include time""" """override default log format to include time"""
return "%(color)s[%(levelname)1.1s %(asctime)s.%(msecs).03d %(name)s %(module)s:%(lineno)d]%(end_color)s %(message)s" return "%(color)s[%(levelname)1.1s %(asctime)s.%(msecs).03d %(name)s %(module)s:%(lineno)d]%(end_color)s %(message)s"
@@ -537,7 +561,7 @@ class JupyterHub(Application):
def init_secrets(self): def init_secrets(self):
trait_name = 'cookie_secret' trait_name = 'cookie_secret'
trait = self.traits()[trait_name] trait = self.traits()[trait_name]
env_name = trait.get_metadata('env') env_name = trait.metadata.get('env')
secret_file = os.path.abspath( secret_file = os.path.abspath(
os.path.expanduser(self.cookie_secret_file) os.path.expanduser(self.cookie_secret_file)
) )

View File

@@ -15,7 +15,7 @@ from tornado import gen
import pamela import pamela
from traitlets.config import LoggingConfigurable from traitlets.config import LoggingConfigurable
from traitlets import Bool, Set, Unicode, Dict, Any from traitlets import Bool, Set, Unicode, Dict, Any, default, observe
from .handlers.login import LoginHandler from .handlers.login import LoginHandler
from .utils import url_path_join from .utils import url_path_join
@@ -61,10 +61,11 @@ class Authenticator(LoggingConfigurable):
If not defined: allow any username. If not defined: allow any username.
""" """
).tag(config=True) ).tag(config=True)
def _username_pattern_changed(self, name, old, new): @observe('username_pattern')
if not new: def _username_pattern_changed(self, change):
if not change['new']:
self.username_regex = None self.username_regex = None
self.username_regex = re.compile(new) self.username_regex = re.compile(change['new'])
username_regex = Any() username_regex = Any()
@@ -272,6 +273,8 @@ class LocalAuthenticator(Authenticator):
when the user 'river' is created. when the user 'river' is created.
""" """
).tag(config=True) ).tag(config=True)
@default('add_user_cmd')
def _add_user_cmd_default(self): def _add_user_cmd_default(self):
if sys.platform == 'darwin': if sys.platform == 'darwin':
raise ValueError("I don't know how to create users on OS X") raise ValueError("I don't know how to create users on OS X")
@@ -285,10 +288,10 @@ class LocalAuthenticator(Authenticator):
group_whitelist = Set( group_whitelist = Set(
help="Automatically whitelist anyone in this group.", help="Automatically whitelist anyone in this group.",
).tag(config=True) ).tag(config=True)
@observe('group_whitelist')
def _group_whitelist_changed(self, name, old, new): def _group_whitelist_changed(self, change):
if self.whitelist: if self.whitelist:
self.log.warn( self.log.warning(
"Ignoring username whitelist because group whitelist supplied!" "Ignoring username whitelist because group whitelist supplied!"
) )

View File

@@ -128,7 +128,7 @@ class Spawner(LoggingConfigurable):
cmd = Command(['jupyterhub-singleuser'], cmd = Command(['jupyterhub-singleuser'],
help="""The command used for starting notebooks.""" help="""The command used for starting notebooks."""
).tag(config=True) ).tag(config=True)
args = List(Unicode, args = List(Unicode(),
help="""Extra arguments to be passed to the single-user server""" help="""Extra arguments to be passed to the single-user server"""
).tag(config=True) ).tag(config=True)

View File

@@ -13,6 +13,8 @@ from tornado import gen
from tornado.concurrent import Future from tornado.concurrent import Future
from tornado.ioloop import IOLoop from tornado.ioloop import IOLoop
from traitlets import default
from ..app import JupyterHub from ..app import JupyterHub
from ..auth import PAMAuthenticator from ..auth import PAMAuthenticator
from .. import orm from .. import orm
@@ -44,7 +46,7 @@ class MockSpawner(LocalProcessSpawner):
def user_env(self, env): def user_env(self, env):
return env return env
@default('cmd')
def _cmd_default(self): def _cmd_default(self):
return [sys.executable, '-m', 'jupyterhub.tests.mocksu'] return [sys.executable, '-m', 'jupyterhub.tests.mocksu']
@@ -66,6 +68,7 @@ class SlowSpawner(MockSpawner):
class NeverSpawner(MockSpawner): class NeverSpawner(MockSpawner):
"""A spawner that will never start""" """A spawner that will never start"""
@default('start_timeout')
def _start_timeout_default(self): def _start_timeout_default(self):
return 1 return 1
@@ -90,6 +93,7 @@ class FormSpawner(MockSpawner):
class MockPAMAuthenticator(PAMAuthenticator): class MockPAMAuthenticator(PAMAuthenticator):
@default('admin_users')
def _admin_users_default(self): def _admin_users_default(self):
return {'admin'} return {'admin'}
@@ -113,15 +117,19 @@ class MockHub(JupyterHub):
last_activity_interval = 2 last_activity_interval = 2
@default('subdomain_host')
def _subdomain_host_default(self): def _subdomain_host_default(self):
return os.environ.get('JUPYTERHUB_TEST_SUBDOMAIN_HOST', '') return os.environ.get('JUPYTERHUB_TEST_SUBDOMAIN_HOST', '')
@default('ip')
def _ip_default(self): def _ip_default(self):
return '127.0.0.1' return '127.0.0.1'
@default('authenticator_class')
def _authenticator_class_default(self): def _authenticator_class_default(self):
return MockPAMAuthenticator return MockPAMAuthenticator
@default('spawner_class')
def _spawner_class_default(self): def _spawner_class_default(self):
return MockSpawner return MockSpawner

View File

@@ -21,7 +21,7 @@ class Command(List):
kwargs.setdefault('minlen', 1) kwargs.setdefault('minlen', 1)
if isinstance(default_value, str): if isinstance(default_value, str):
default_value = [default_value] default_value = [default_value]
super().__init__(Unicode, default_value, **kwargs) super().__init__(Unicode(), default_value, **kwargs)
def validate(self, obj, value): def validate(self, obj, value):
if isinstance(value, str): if isinstance(value, str):

View File

@@ -12,7 +12,7 @@ from sqlalchemy import inspect
from .utils import url_path_join from .utils import url_path_join
from . import orm from . import orm
from traitlets import HasTraits, Any, Dict from traitlets import HasTraits, Any, Dict, observe, default
from .spawner import LocalProcessSpawner from .spawner import LocalProcessSpawner
@@ -41,7 +41,7 @@ class UserDict(dict):
elif isinstance(key, str): elif isinstance(key, str):
orm_user = self.db.query(orm.User).filter(orm.User.name==key).first() orm_user = self.db.query(orm.User).filter(orm.User.name==key).first()
if orm_user is None: if orm_user is None:
raise KeyError("No such user: %s" % name) raise KeyError("No such user: %s" % key)
else: else:
key = orm_user key = orm_user
if isinstance(key, orm.User): if isinstance(key, orm.User):
@@ -75,22 +75,24 @@ class UserDict(dict):
class User(HasTraits): class User(HasTraits):
@default('log')
def _log_default(self): def _log_default(self):
return app_log return app_log
settings = Dict() settings = Dict()
db = Any(allow_none=True) db = Any(allow_none=True)
@default('db')
def _db_default(self): def _db_default(self):
if self.orm_user: if self.orm_user:
return inspect(self.orm_user).session return inspect(self.orm_user).session
@observe('db')
def _db_changed(self, name, old, new): def _db_changed(self, change):
"""Changing db session reacquires ORM User object""" """Changing db session reacquires ORM User object"""
# db session changed, re-get orm User # db session changed, re-get orm User
if self.orm_user: if self.orm_user:
id = self.orm_user.id id = self.orm_user.id
self.orm_user = new.query(orm.User).filter(orm.User.id==id).first() self.orm_user = change['new'].query(orm.User).filter(orm.User.id==id).first()
self.spawner.db = self.db self.spawner.db = self.db
orm_user = None orm_user = None