From 7a2794af7c93ee337171d5baed6fe0b9d336b79f Mon Sep 17 00:00:00 2001 From: Min RK Date: Sun, 27 Mar 2016 10:41:36 -0700 Subject: [PATCH] use traitlets-4.1 observe/default decorators --- jupyterhub/app.py | 32 ++++++++++++++++++++++++++++---- jupyterhub/auth.py | 17 ++++++++++------- jupyterhub/spawner.py | 2 +- jupyterhub/tests/mocking.py | 10 +++++++++- jupyterhub/traitlets.py | 2 +- jupyterhub/user.py | 12 +++++++----- 6 files changed, 56 insertions(+), 19 deletions(-) diff --git a/jupyterhub/app.py b/jupyterhub/app.py index 66ff6fcf..b5eeb729 100644 --- a/jupyterhub/app.py +++ b/jupyterhub/app.py @@ -36,6 +36,7 @@ from tornado import gen, web from traitlets import ( Unicode, Integer, Dict, TraitError, List, Bool, Any, Type, Set, Instance, Bytes, Float, + observe, default, ) from traitlets.config import Application, catch_config_error @@ -209,6 +210,7 @@ class JupyterHub(Application): help="Paths to search for jinja templates.", ).tag(config=True) + @default('template_paths') def _template_paths_default(self): return [os.path.join(self.data_files_path, 'templates')] @@ -262,6 +264,8 @@ class JupyterHub(Application): logo_file = Unicode('', help="Specify path to a logo image to override the Jupyter logo in the banner." ).tag(config=True) + + @default('logo_file') def _logo_file_default(self): 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. """ ).tag(config=True) + + @default('proxy_auth_token') def _proxy_auth_token_default(self): token = os.environ.get('CONFIGPROXY_AUTH_TOKEN', None) if not token: @@ -302,6 +308,8 @@ class JupyterHub(Application): proxy_api_port = Integer( help="The port for the proxy API handlers" ).tag(config=True) + + @default('proxy_api_port') def _proxy_api_port_default(self): return self.port + 1 @@ -314,21 +322,27 @@ class JupyterHub(Application): hub_prefix = URLPrefix('/hub/', help="The prefix for the hub server. Must not be '/'" ).tag(config=True) + + @default('hub_prefix') def _hub_prefix_default(self): return url_path_join(self.base_url, '/hub/') + @observe('hub_prefix') def _hub_prefix_changed(self, name, old, new): if new == '/': raise TraitError("'/' is not a valid hub prefix") if not new.startswith(self.base_url): 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. 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', help="""File in which to store the cookie secret.""" @@ -350,6 +364,8 @@ class JupyterHub(Application): ).tag(config=True) authenticator = Instance(Authenticator) + + @default('authenticator') def _authenticator_default(self): return self.authenticator_class(parent=self, db=self.db) @@ -364,7 +380,10 @@ class JupyterHub(Application): db_url = Unicode('sqlite:///jupyterhub.sqlite', help="url for the database. e.g. `sqlite:///jupyterhub.sqlite`" ).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: # assume sqlite, if given as a plain filename self.db_url = 'sqlite:///%s' % new @@ -384,6 +403,8 @@ class JupyterHub(Application): session_factory = Any() users = Instance(UserDict) + + @default('users') def _users_default(self): assert self.tornado_settings return UserDict(db_factory=lambda : self.db, settings=self.tornado_settings) @@ -435,13 +456,16 @@ class JupyterHub(Application): proxy_process = None io_loop = None + @default('log_level') def _log_level_default(self): return logging.INFO + @default('log_datefmt') def _log_datefmt_default(self): """Exclude date from default date format""" return "%Y-%m-%d %H:%M:%S" + @default('log_format') def _log_format_default(self): """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" @@ -537,7 +561,7 @@ class JupyterHub(Application): def init_secrets(self): trait_name = 'cookie_secret' trait = self.traits()[trait_name] - env_name = trait.get_metadata('env') + env_name = trait.metadata.get('env') secret_file = os.path.abspath( os.path.expanduser(self.cookie_secret_file) ) diff --git a/jupyterhub/auth.py b/jupyterhub/auth.py index 044af6a8..08f4cd90 100644 --- a/jupyterhub/auth.py +++ b/jupyterhub/auth.py @@ -15,7 +15,7 @@ from tornado import gen import pamela 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 .utils import url_path_join @@ -61,10 +61,11 @@ class Authenticator(LoggingConfigurable): If not defined: allow any username. """ ).tag(config=True) - def _username_pattern_changed(self, name, old, new): - if not new: + @observe('username_pattern') + def _username_pattern_changed(self, change): + if not change['new']: self.username_regex = None - self.username_regex = re.compile(new) + self.username_regex = re.compile(change['new']) username_regex = Any() @@ -272,6 +273,8 @@ class LocalAuthenticator(Authenticator): when the user 'river' is created. """ ).tag(config=True) + + @default('add_user_cmd') def _add_user_cmd_default(self): if sys.platform == 'darwin': raise ValueError("I don't know how to create users on OS X") @@ -285,10 +288,10 @@ class LocalAuthenticator(Authenticator): group_whitelist = Set( help="Automatically whitelist anyone in this group.", ).tag(config=True) - - def _group_whitelist_changed(self, name, old, new): + @observe('group_whitelist') + def _group_whitelist_changed(self, change): if self.whitelist: - self.log.warn( + self.log.warning( "Ignoring username whitelist because group whitelist supplied!" ) diff --git a/jupyterhub/spawner.py b/jupyterhub/spawner.py index 713047d9..68badf11 100644 --- a/jupyterhub/spawner.py +++ b/jupyterhub/spawner.py @@ -128,7 +128,7 @@ class Spawner(LoggingConfigurable): cmd = Command(['jupyterhub-singleuser'], help="""The command used for starting notebooks.""" ).tag(config=True) - args = List(Unicode, + args = List(Unicode(), help="""Extra arguments to be passed to the single-user server""" ).tag(config=True) diff --git a/jupyterhub/tests/mocking.py b/jupyterhub/tests/mocking.py index 20e97cee..7593d84d 100644 --- a/jupyterhub/tests/mocking.py +++ b/jupyterhub/tests/mocking.py @@ -13,6 +13,8 @@ from tornado import gen from tornado.concurrent import Future from tornado.ioloop import IOLoop +from traitlets import default + from ..app import JupyterHub from ..auth import PAMAuthenticator from .. import orm @@ -44,7 +46,7 @@ class MockSpawner(LocalProcessSpawner): def user_env(self, env): return env - + @default('cmd') def _cmd_default(self): return [sys.executable, '-m', 'jupyterhub.tests.mocksu'] @@ -66,6 +68,7 @@ class SlowSpawner(MockSpawner): class NeverSpawner(MockSpawner): """A spawner that will never start""" + @default('start_timeout') def _start_timeout_default(self): return 1 @@ -90,6 +93,7 @@ class FormSpawner(MockSpawner): class MockPAMAuthenticator(PAMAuthenticator): + @default('admin_users') def _admin_users_default(self): return {'admin'} @@ -113,15 +117,19 @@ class MockHub(JupyterHub): last_activity_interval = 2 + @default('subdomain_host') def _subdomain_host_default(self): return os.environ.get('JUPYTERHUB_TEST_SUBDOMAIN_HOST', '') + @default('ip') def _ip_default(self): return '127.0.0.1' + @default('authenticator_class') def _authenticator_class_default(self): return MockPAMAuthenticator + @default('spawner_class') def _spawner_class_default(self): return MockSpawner diff --git a/jupyterhub/traitlets.py b/jupyterhub/traitlets.py index f9542747..2daa9425 100644 --- a/jupyterhub/traitlets.py +++ b/jupyterhub/traitlets.py @@ -21,7 +21,7 @@ class Command(List): kwargs.setdefault('minlen', 1) if isinstance(default_value, str): default_value = [default_value] - super().__init__(Unicode, default_value, **kwargs) + super().__init__(Unicode(), default_value, **kwargs) def validate(self, obj, value): if isinstance(value, str): diff --git a/jupyterhub/user.py b/jupyterhub/user.py index 8cf13bf6..fdadaabc 100644 --- a/jupyterhub/user.py +++ b/jupyterhub/user.py @@ -12,7 +12,7 @@ from sqlalchemy import inspect from .utils import url_path_join from . import orm -from traitlets import HasTraits, Any, Dict +from traitlets import HasTraits, Any, Dict, observe, default from .spawner import LocalProcessSpawner @@ -41,7 +41,7 @@ class UserDict(dict): elif isinstance(key, str): orm_user = self.db.query(orm.User).filter(orm.User.name==key).first() if orm_user is None: - raise KeyError("No such user: %s" % name) + raise KeyError("No such user: %s" % key) else: key = orm_user if isinstance(key, orm.User): @@ -75,22 +75,24 @@ class UserDict(dict): class User(HasTraits): + @default('log') def _log_default(self): return app_log settings = Dict() db = Any(allow_none=True) + @default('db') def _db_default(self): if self.orm_user: return inspect(self.orm_user).session - - def _db_changed(self, name, old, new): + @observe('db') + def _db_changed(self, change): """Changing db session reacquires ORM User object""" # db session changed, re-get orm User if self.orm_user: 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 orm_user = None