From fa486200767e3e50b94d7925f1621c3562eed731 Mon Sep 17 00:00:00 2001 From: Min RK Date: Sun, 27 Mar 2016 10:29:36 -0700 Subject: [PATCH] use traitlets-4.1 `.tag(config=True)` API --- jupyterhub/app.py | 161 +++++++++++++++++++++--------------------- jupyterhub/auth.py | 39 +++++----- jupyterhub/spawner.py | 65 +++++++++-------- 3 files changed, 131 insertions(+), 134 deletions(-) diff --git a/jupyterhub/app.py b/jupyterhub/app.py index 010f91b8..66ff6fcf 100644 --- a/jupyterhub/app.py +++ b/jupyterhub/app.py @@ -175,66 +175,65 @@ class JupyterHub(Application): PAMAuthenticator, ]) - config_file = Unicode('jupyterhub_config.py', config=True, + config_file = Unicode('jupyterhub_config.py', help="The config file to load", - ) - generate_config = Bool(False, config=True, + ).tag(config=True) + generate_config = Bool(False, help="Generate default config file", - ) - answer_yes = Bool(False, config=True, + ).tag(config=True) + answer_yes = Bool(False, help="Answer yes to any questions (e.g. confirm overwrite)" - ) - pid_file = Unicode('', config=True, + ).tag(config=True) + pid_file = Unicode('', help="""File to write PID Useful for daemonizing jupyterhub. """ - ) - cookie_max_age_days = Float(14, config=True, + ).tag(config=True) + cookie_max_age_days = Float(14, help="""Number of days for a login cookie to be valid. Default is two weeks. """ - ) - last_activity_interval = Integer(300, config=True, + ).tag(config=True) + last_activity_interval = Integer(300, help="Interval (in seconds) at which to update last-activity timestamps." - ) - proxy_check_interval = Integer(30, config=True, + ).tag(config=True) + proxy_check_interval = Integer(30, help="Interval (in seconds) at which to check if the proxy is running." - ) + ).tag(config=True) - data_files_path = Unicode(DATA_FILES_PATH, config=True, + data_files_path = Unicode(DATA_FILES_PATH, help="The location of jupyterhub data files (e.g. /usr/local/share/jupyter/hub)" - ) + ).tag(config=True) template_paths = List( - config=True, help="Paths to search for jinja templates.", - ) + ).tag(config=True) def _template_paths_default(self): return [os.path.join(self.data_files_path, 'templates')] - confirm_no_ssl = Bool(False, config=True, + confirm_no_ssl = Bool(False, help="""Confirm that JupyterHub should be run without SSL. This is **NOT RECOMMENDED** unless SSL termination is being handled by another layer. """ - ) - ssl_key = Unicode('', config=True, + ).tag(config=True) + ssl_key = Unicode('', help="""Path to SSL key file for the public facing interface of the proxy Use with ssl_cert """ - ) - ssl_cert = Unicode('', config=True, + ).tag(config=True) + ssl_cert = Unicode('', help="""Path to SSL certificate file for the public facing interface of the proxy Use with ssl_key """ - ) - ip = Unicode('', config=True, + ).tag(config=True) + ip = Unicode('', help="The public facing ip of the whole application (the proxy)" - ) + ).tag(config=True) - subdomain_host = Unicode('', config=True, + subdomain_host = Unicode('', help="""Run single-user servers on subdomains of this host. This should be the full https://hub.domain.tld[:port] @@ -246,42 +245,45 @@ class JupyterHub(Application): In general, this is most easily achieved with wildcard DNS. When using SSL (i.e. always) this also requires a wildcard SSL certificate. - """) + """ + ).tag(config=True) def _subdomain_host_changed(self, name, old, new): if new and '://' not in new: # host should include '://' # if not specified, assume https: You have to be really explicit about HTTP! self.subdomain_host = 'https://' + new - port = Integer(8000, config=True, + port = Integer(8000, help="The public facing port of the proxy" - ) - base_url = URLPrefix('/', config=True, + ).tag(config=True) + base_url = URLPrefix('/', help="The base URL of the entire application" - ) - logo_file = Unicode('', config=True, + ).tag(config=True) + logo_file = Unicode('', help="Specify path to a logo image to override the Jupyter logo in the banner." - ) + ).tag(config=True) def _logo_file_default(self): return os.path.join(self.data_files_path, 'static', 'images', 'jupyter.png') - jinja_environment_options = Dict(config=True, + jinja_environment_options = Dict( help="Supply extra arguments that will be passed to Jinja environment." - ) + ).tag(config=True) - proxy_cmd = Command('configurable-http-proxy', config=True, + proxy_cmd = Command('configurable-http-proxy', help="""The command to start the http proxy. Only override if configurable-http-proxy is not on your PATH """ - ) - debug_proxy = Bool(False, config=True, help="show debug output in configurable-http-proxy") - proxy_auth_token = Unicode(config=True, + ).tag(config=True) + debug_proxy = Bool(False, + help="show debug output in configurable-http-proxy" + ).tag(config=True) + proxy_auth_token = Unicode( help="""The Proxy Auth token. Loaded from the CONFIGPROXY_AUTH_TOKEN env variable by default. """ - ) + ).tag(config=True) def _proxy_auth_token_default(self): token = os.environ.get('CONFIGPROXY_AUTH_TOKEN', None) if not token: @@ -294,24 +296,24 @@ class JupyterHub(Application): token = orm.new_token() return token - proxy_api_ip = Unicode('127.0.0.1', config=True, + proxy_api_ip = Unicode('127.0.0.1', help="The ip for the proxy API handlers" - ) - proxy_api_port = Integer(config=True, + ).tag(config=True) + proxy_api_port = Integer( help="The port for the proxy API handlers" - ) + ).tag(config=True) def _proxy_api_port_default(self): return self.port + 1 - hub_port = Integer(8081, config=True, + hub_port = Integer(8081, help="The port for this process" - ) - hub_ip = Unicode('127.0.0.1', config=True, + ).tag(config=True) + hub_ip = Unicode('127.0.0.1', help="The ip for this process" - ) - hub_prefix = URLPrefix('/hub/', config=True, + ).tag(config=True) + hub_prefix = URLPrefix('/hub/', help="The prefix for the hub server. Must not be '/'" - ) + ).tag(config=True) def _hub_prefix_default(self): return url_path_join(self.base_url, '/hub/') @@ -321,19 +323,18 @@ class JupyterHub(Application): if not new.startswith(self.base_url): self.hub_prefix = url_path_join(self.base_url, new) - cookie_secret = Bytes(config=True, env='JPY_COOKIE_SECRET', + cookie_secret = Bytes(env='JPY_COOKIE_SECRET', help="""The cookie secret to use to encrypt cookies. Loaded from the JPY_COOKIE_SECRET env variable by default. """ - ) + ).tag(config=True) - cookie_secret_file = Unicode('jupyterhub_cookie_secret', config=True, + cookie_secret_file = Unicode('jupyterhub_cookie_secret', help="""File in which to store the cookie secret.""" - ) + ).tag(config=True) authenticator_class = Type(PAMAuthenticator, Authenticator, - config=True, help="""Class for authenticating users. This should be a class with the following form: @@ -346,7 +347,7 @@ class JupyterHub(Application): where `handler` is the calling web.RequestHandler, and `data` is the POST form data from the login page. """ - ) + ).tag(config=True) authenticator = Instance(Authenticator) def _authenticator_default(self): @@ -354,33 +355,32 @@ class JupyterHub(Application): # class for spawning single-user servers spawner_class = Type(LocalProcessSpawner, Spawner, - config=True, help="""The class to use for spawning single-user servers. Should be a subclass of Spawner. """ - ) + ).tag(config=True) - db_url = Unicode('sqlite:///jupyterhub.sqlite', config=True, + 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): if '://' not in new: # assume sqlite, if given as a plain filename self.db_url = 'sqlite:///%s' % new - db_kwargs = Dict(config=True, + db_kwargs = Dict( help="""Include any kwargs to pass to the database connection. See sqlalchemy.create_engine for details. """ - ) + ).tag(config=True) - reset_db = Bool(False, config=True, + reset_db = Bool(False, help="Purge and reset the database." - ) - debug_db = Bool(False, config=True, + ).tag(config=True) + debug_db = Bool(False, help="log all database transactions. This has A LOT of output" - ) + ).tag(config=True) session_factory = Any() users = Instance(UserDict) @@ -388,19 +388,21 @@ class JupyterHub(Application): assert self.tornado_settings return UserDict(db_factory=lambda : self.db, settings=self.tornado_settings) - admin_access = Bool(False, config=True, + admin_access = Bool(False, help="""Grant admin users permission to access single-user servers. Users should be properly informed if this is enabled. """ - ) - admin_users = Set(config=True, + ).tag(config=True) + admin_users = Set( help="""DEPRECATED, use Authenticator.admin_users instead.""" - ) + ).tag(config=True) - tornado_settings = Dict(config=True) + tornado_settings = Dict( + help="Extra settings overrides to pass to the tornado application." + ).tag(config=True) - cleanup_servers = Bool(True, config=True, + cleanup_servers = Bool(True, help="""Whether to shutdown single-user servers when the Hub shuts down. Disable if you want to be able to teardown the Hub while leaving the single-user servers running. @@ -410,9 +412,9 @@ class JupyterHub(Application): The Hub should be able to resume from database state. """ - ) + ).tag(config=True) - cleanup_proxy = Bool(True, config=True, + cleanup_proxy = Bool(True, help="""Whether to shutdown the proxy when the Hub shuts down. Disable if you want to be able to teardown the Hub while leaving the proxy running. @@ -424,7 +426,7 @@ class JupyterHub(Application): The Hub should be able to resume from database state. """ - ) + ).tag(config=True) handlers = List() @@ -445,15 +447,12 @@ class JupyterHub(Application): return "%(color)s[%(levelname)1.1s %(asctime)s.%(msecs).03d %(name)s %(module)s:%(lineno)d]%(end_color)s %(message)s" extra_log_file = Unicode( - "", - config=True, help="Set a logging.FileHandler on this file." - ) + ).tag(config=True) extra_log_handlers = List( Instance(logging.Handler), - config=True, help="Extra log handlers to set on JupyterHub logger", - ) + ).tag(config=True) def init_logging(self): # This prevents double log messages because tornado use a root logger that diff --git a/jupyterhub/auth.py b/jupyterhub/auth.py index e8b0bcd1..044af6a8 100644 --- a/jupyterhub/auth.py +++ b/jupyterhub/auth.py @@ -29,19 +29,19 @@ class Authenticator(LoggingConfigurable): """ db = Any() - admin_users = Set(config=True, + admin_users = Set( help="""set of usernames of admin users If unspecified, only the user that launches the server will be admin. """ - ) - whitelist = Set(config=True, + ).tag(config=True) + whitelist = Set( help="""Username whitelist. Use this to restrict which users can login. If empty, allow any user to attempt login. """ - ) + ).tag(config=True) custom_html = Unicode('', help="""HTML login form for custom handlers. Override in form-based custom authenticators @@ -55,12 +55,12 @@ class Authenticator(LoggingConfigurable): """ ) - username_pattern = Unicode(config=True, + username_pattern = Unicode( help="""Regular expression pattern for validating usernames. If not defined: allow any username. """ - ) + ).tag(config=True) def _username_pattern_changed(self, name, old, new): if not new: self.username_regex = None @@ -77,14 +77,14 @@ class Authenticator(LoggingConfigurable): return True return bool(self.username_regex.match(username)) - username_map = Dict(config=True, + username_map = Dict( help="""Dictionary mapping authenticator usernames to JupyterHub users. Can be used to map OAuth service names to local users, for instance. Used in normalize_username. """ - ) + ).tag(config=True) def normalize_username(self, username): """Normalize a username. @@ -246,12 +246,12 @@ class LocalAuthenticator(Authenticator): Checks for local users, and can attempt to create them if they exist. """ - create_system_users = Bool(False, config=True, + create_system_users = Bool(False, help="""If a user is added that doesn't exist on the system, should I try to create the system user? """ - ) - add_user_cmd = Command(config=True, + ).tag(config=True) + add_user_cmd = Command( help="""The command to use for creating users as a list of strings. For each element in the list, the string USERNAME will be replaced with @@ -271,7 +271,7 @@ class LocalAuthenticator(Authenticator): when the user 'river' is created. """ - ) + ).tag(config=True) def _add_user_cmd_default(self): if sys.platform == 'darwin': raise ValueError("I don't know how to create users on OS X") @@ -283,9 +283,8 @@ class LocalAuthenticator(Authenticator): return ['adduser', '-q', '--gecos', '""', '--disabled-password'] group_whitelist = Set( - config=True, help="Automatically whitelist anyone in this group.", - ) + ).tag(config=True) def _group_whitelist_changed(self, name, old, new): if self.whitelist: @@ -351,13 +350,13 @@ class LocalAuthenticator(Authenticator): class PAMAuthenticator(LocalAuthenticator): """Authenticate local Linux/UNIX users with PAM""" - encoding = Unicode('utf8', config=True, + encoding = Unicode('utf8', help="""The encoding to use for PAM""" - ) - service = Unicode('login', config=True, + ).tag(config=True) + service = Unicode('login', help="""The PAM service to use for authentication.""" - ) - open_sessions = Bool(True, config=True, + ).tag(config=True) + open_sessions = Bool(True, help="""Whether to open PAM sessions when spawners are started. This may trigger things like mounting shared filsystems, @@ -368,7 +367,7 @@ class PAMAuthenticator(LocalAuthenticator): c.PAMAuthenticator.open_sessions = False """ - ) + ).tag(config=True) @gen.coroutine def authenticate(self, handler, data): diff --git a/jupyterhub/spawner.py b/jupyterhub/spawner.py index 685679dc..713047d9 100644 --- a/jupyterhub/spawner.py +++ b/jupyterhub/spawner.py @@ -41,39 +41,38 @@ class Spawner(LoggingConfigurable): hub = Any() authenticator = Any() api_token = Unicode() - ip = Unicode('127.0.0.1', config=True, + ip = Unicode('127.0.0.1', help="The IP address (or hostname) the single-user server should listen on" - ) - start_timeout = Integer(60, config=True, + ).tag(config=True) + start_timeout = Integer(60, help="""Timeout (in seconds) before giving up on the spawner. This is the timeout for start to return, not the timeout for the server to respond. Callers of spawner.start will assume that startup has failed if it takes longer than this. start should return when the server process is started and its location is known. """ - ) + ).tag(config=True) - http_timeout = Integer( - 30, config=True, + http_timeout = Integer(30, help="""Timeout (in seconds) before giving up on a spawned HTTP server Once a server has successfully been spawned, this is the amount of time we wait before assuming that the server is unable to accept connections. """ - ) + ).tag(config=True) - poll_interval = Integer(30, config=True, + poll_interval = Integer(30, help="""Interval (in seconds) on which to poll the spawner.""" - ) + ).tag(config=True) _callbacks = List() _poll_callback = Any() - debug = Bool(False, config=True, + debug = Bool(False, help="Enable debug-logging of the single-user server" - ) + ).tag(config=True) - options_form = Unicode("", config=True, help=""" + options_form = Unicode("", help=""" An HTML form for options a user can specify on launching their server. The surrounding `
` element and the submit button are already provided. @@ -87,7 +86,7 @@ class Spawner(LoggingConfigurable): - """) + """).tag(config=True) def options_from_form(self, form_data): """Interpret HTTP form data @@ -113,35 +112,35 @@ class Spawner(LoggingConfigurable): 'VIRTUAL_ENV', 'LANG', 'LC_ALL', - ], config=True, + ], help="Whitelist of environment variables for the subprocess to inherit" - ) + ).tag(config=True) env = Dict(help="""Deprecated: use Spawner.get_env or Spawner.environment - extend Spawner.get_env for adding required env in Spawner subclasses - Spawner.environment for config-specified env """) - environment = Dict(config=True, + environment = Dict( help="Environment variables to load for the Spawner." - ) + ).tag(config=True) - cmd = Command(['jupyterhub-singleuser'], config=True, + cmd = Command(['jupyterhub-singleuser'], help="""The command used for starting notebooks.""" - ) - args = List(Unicode, config=True, + ).tag(config=True) + args = List(Unicode, help="""Extra arguments to be passed to the single-user server""" - ) + ).tag(config=True) - notebook_dir = Unicode('', config=True, + notebook_dir = Unicode('', help="""The notebook directory for the single-user server `~` will be expanded to the user's home directory `%U` will be expanded to the user's username """ - ) + ).tag(config=True) - default_url = Unicode('', config=True, + default_url = Unicode('', help="""The default URL for the single-user server. Can be used in conjunction with --notebook-dir=/ to enable @@ -150,15 +149,15 @@ class Spawner(LoggingConfigurable): `%U` will be expanded to the user's username """ - ) + ).tag(config=True) - disable_user_config = Bool(False, config=True, + disable_user_config = Bool(False, help="""Disable per-user configuration of single-user servers. This prevents any config in users' $HOME directories from having an effect on their server. """ - ) + ).tag(config=True) def __init__(self, **kwargs): super(Spawner, self).__init__(**kwargs) @@ -385,15 +384,15 @@ class LocalProcessSpawner(Spawner): This is the default spawner for JupyterHub. """ - INTERRUPT_TIMEOUT = Integer(10, config=True, + INTERRUPT_TIMEOUT = Integer(10, help="Seconds to wait for process to halt after SIGINT before proceeding to SIGTERM" - ) - TERM_TIMEOUT = Integer(5, config=True, + ).tag(config=True) + TERM_TIMEOUT = Integer(5, help="Seconds to wait for process to halt after SIGTERM before proceeding to SIGKILL" - ) - KILL_TIMEOUT = Integer(5, config=True, + ).tag(config=True) + KILL_TIMEOUT = Integer(5, help="Seconds to wait for process to halt after SIGKILL before giving up" - ) + ).tag(config=True) proc = Instance(Popen, allow_none=True) pid = Integer(0)