We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Single user server launch is broken
I think that #261 broke the launching of the single user server. I am seeing the following errors in the nbgrader tests:

```
Traceback (most recent call last):
  File "/Users/jhamrick/.virtualenvs/nbgrader/bin/jupyterhub-singleuser", line 6, in <module>
    exec(compile(open(__file__).read(), __file__, 'exec'))
  File "/Users/jhamrick/project/tools/jupyterhub/scripts/jupyterhub-singleuser", line 4, in <module>
    main()
  File "/Users/jhamrick/project/tools/jupyterhub/jupyterhub/singleuser.py", line 221, in main
    return SingleUserNotebookApp.launch_instance()
  File "/Users/jhamrick/.virtualenvs/nbgrader/lib/python3.4/site-packages/IPython/config/application.py", line 573, in launch_instance
    app.initialize(argv)
  File "<string>", line 2, in initialize
  File "/Users/jhamrick/.virtualenvs/nbgrader/lib/python3.4/site-packages/IPython/config/application.py", line 75, in catch_config_error
    return method(app, *args, **kwargs)
  File "/Users/jhamrick/.virtualenvs/nbgrader/lib/python3.4/site-packages/IPython/html/notebookapp.py", line 1015, in initialize
    self.init_webapp()
  File "/Users/jhamrick/project/tools/jupyterhub/jupyterhub/singleuser.py", line 191, in init_webapp
    s['user'] = self.user
  File "/Users/jhamrick/.virtualenvs/nbgrader/lib/python3.4/site-packages/traitlets/traitlets.py", line 438, in __get__
    % (self.name, obj))
traitlets.traitlets.TraitError: No default value found for None trait of <jupyterhub.singleuser.SingleUserNotebookApp object at 0x102953b00>
```

If I revert to the version of jupyterhub prior to that PR, this error does not occur. @epifanio reported on gitter seeing the same thing as well, so I don't think it's isolated to nbgrader.

Given the error message, I suspect this has to do with ipython/traitlets#39 and/or ipython/traitlets#40 though I haven't actually tested it. I tried giving the `user` trait a default value but it did not seem to fix the error. I will try to do a bit more debugging, but I fear I don't really understand the internals of traitlets well enough to know exactly what's going on here.

Ping @takluyver and @minrk ?


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `jupyterhub/singleuser.py`
Content:
```
1 #!/usr/bin/env python3
2 """Extend regular notebook server to be aware of multiuser things."""
3 
4 # Copyright (c) Jupyter Development Team.
5 # Distributed under the terms of the Modified BSD License.
6 
7 import os
8 try:
9     from urllib.parse import quote
10 except ImportError:
11     # PY2 Compat
12     from urllib import quote
13 
14 import requests
15 from jinja2 import ChoiceLoader, FunctionLoader
16 
17 from tornado import ioloop
18 from tornado.web import HTTPError
19 
20 from traitlets import (
21     Integer,
22     Unicode,
23     CUnicode,
24 )
25 
26 from IPython.html.notebookapp import NotebookApp, aliases as notebook_aliases
27 from IPython.html.auth.login import LoginHandler
28 from IPython.html.auth.logout import LogoutHandler
29 
30 from IPython.html.utils import url_path_join
31 
32 
33 from distutils.version import LooseVersion as V
34 
35 import IPython
36 if V(IPython.__version__) < V('3.0'):
37     raise ImportError("JupyterHub Requires IPython >= 3.0, found %s" % IPython.__version__)
38 
39 # Define two methods to attach to AuthenticatedHandler,
40 # which authenticate via the central auth server.
41 
42 class JupyterHubLoginHandler(LoginHandler):
43     @staticmethod
44     def login_available(settings):
45         return True
46     
47     @staticmethod
48     def verify_token(self, cookie_name, encrypted_cookie):
49         """method for token verification"""
50         cookie_cache = self.settings['cookie_cache']
51         if encrypted_cookie in cookie_cache:
52             # we've seen this token before, don't ask upstream again
53             return cookie_cache[encrypted_cookie]
54     
55         hub_api_url = self.settings['hub_api_url']
56         hub_api_key = self.settings['hub_api_key']
57         r = requests.get(url_path_join(
58             hub_api_url, "authorizations/cookie", cookie_name, quote(encrypted_cookie, safe=''),
59         ),
60             headers = {'Authorization' : 'token %s' % hub_api_key},
61         )
62         if r.status_code == 404:
63             data = None
64         elif r.status_code == 403:
65             self.log.error("I don't have permission to verify cookies, my auth token may have expired: [%i] %s", r.status_code, r.reason)
66             raise HTTPError(500, "Permission failure checking authorization, I may need to be restarted")
67         elif r.status_code >= 500:
68             self.log.error("Upstream failure verifying auth token: [%i] %s", r.status_code, r.reason)
69             raise HTTPError(502, "Failed to check authorization (upstream problem)")
70         elif r.status_code >= 400:
71             self.log.warn("Failed to check authorization: [%i] %s", r.status_code, r.reason)
72             raise HTTPError(500, "Failed to check authorization")
73         else:
74             data = r.json()
75         cookie_cache[encrypted_cookie] = data
76         return data
77     
78     @staticmethod
79     def get_user(self):
80         """alternative get_current_user to query the central server"""
81         # only allow this to be called once per handler
82         # avoids issues if an error is raised,
83         # since this may be called again when trying to render the error page
84         if hasattr(self, '_cached_user'):
85             return self._cached_user
86         
87         self._cached_user = None
88         my_user = self.settings['user']
89         encrypted_cookie = self.get_cookie(self.cookie_name)
90         if encrypted_cookie:
91             auth_data = JupyterHubLoginHandler.verify_token(self, self.cookie_name, encrypted_cookie)
92             if not auth_data:
93                 # treat invalid token the same as no token
94                 return None
95             user = auth_data['name']
96             if user == my_user:
97                 self._cached_user = user
98                 return user
99             else:
100                 return None
101         else:
102             self.log.debug("No token cookie")
103             return None
104 
105 
106 class JupyterHubLogoutHandler(LogoutHandler):
107     def get(self):
108         self.redirect(url_path_join(self.settings['hub_prefix'], 'logout'))
109 
110 
111 # register new hub related command-line aliases
112 aliases = dict(notebook_aliases)
113 aliases.update({
114     'user' : 'SingleUserNotebookApp.user',
115     'cookie-name': 'SingleUserNotebookApp.cookie_name',
116     'hub-prefix': 'SingleUserNotebookApp.hub_prefix',
117     'hub-api-url': 'SingleUserNotebookApp.hub_api_url',
118     'base-url': 'SingleUserNotebookApp.base_url',
119 })
120 
121 page_template = """
122 {% extends "templates/page.html" %}
123 
124 {% block header_buttons %}
125 {{super()}}
126 
127 <a href='{{hub_control_panel_url}}'
128  class='btn btn-default btn-sm navbar-btn pull-right'
129  style='margin-right: 4px; margin-left: 2px;'
130 >
131 Control Panel</a>
132 {% endblock %}
133 """
134 
135 class SingleUserNotebookApp(NotebookApp):
136     """A Subclass of the regular NotebookApp that is aware of the parent multiuser context."""
137     user = CUnicode(config=True)
138     def _user_changed(self, name, old, new):
139         self.log.name = new
140     cookie_name = Unicode(config=True)
141     hub_prefix = Unicode(config=True)
142     hub_api_url = Unicode(config=True)
143     aliases = aliases
144     open_browser = False
145     trust_xheaders = True
146     login_handler_class = JupyterHubLoginHandler
147     logout_handler_class = JupyterHubLogoutHandler
148 
149     cookie_cache_lifetime = Integer(
150         config=True,
151         default_value=300,
152         allow_none=True,
153         help="""
154         Time, in seconds, that we cache a validated cookie before requiring
155         revalidation with the hub.
156         """,
157     )
158 
159     def _log_datefmt_default(self):
160         """Exclude date from default date format"""
161         return "%Y-%m-%d %H:%M:%S"
162 
163     def _log_format_default(self):
164         """override default log format to include time"""
165         return "%(color)s[%(levelname)1.1s %(asctime)s.%(msecs).03d %(name)s %(module)s:%(lineno)d]%(end_color)s %(message)s"
166 
167     def _confirm_exit(self):
168         # disable the exit confirmation for background notebook processes
169         ioloop.IOLoop.instance().stop()
170 
171     def _clear_cookie_cache(self):
172         self.log.debug("Clearing cookie cache")
173         self.tornado_settings['cookie_cache'].clear()
174     
175     def start(self):
176         # Start a PeriodicCallback to clear cached cookies.  This forces us to
177         # revalidate our user with the Hub at least every
178         # `cookie_cache_lifetime` seconds.
179         if self.cookie_cache_lifetime:
180             ioloop.PeriodicCallback(
181                 self._clear_cookie_cache,
182                 self.cookie_cache_lifetime * 1e3,
183             ).start()
184         super(SingleUserNotebookApp, self).start()
185     
186     def init_webapp(self):
187         # load the hub related settings into the tornado settings dict
188         env = os.environ
189         s = self.tornado_settings
190         s['cookie_cache'] = {}
191         s['user'] = self.user
192         s['hub_api_key'] = env.pop('JPY_API_TOKEN')
193         s['hub_prefix'] = self.hub_prefix
194         s['cookie_name'] = self.cookie_name
195         s['login_url'] = self.hub_prefix
196         s['hub_api_url'] = self.hub_api_url
197         
198         super(SingleUserNotebookApp, self).init_webapp()
199         self.patch_templates()
200     
201     def patch_templates(self):
202         """Patch page templates to add Hub-related buttons"""
203         env = self.web_app.settings['jinja2_env']
204         
205         env.globals['hub_control_panel_url'] = \
206             url_path_join(self.hub_prefix, 'home')
207         
208         # patch jinja env loading to modify page template
209         def get_page(name):
210             if name == 'page.html':
211                 return page_template
212         
213         orig_loader = env.loader
214         env.loader = ChoiceLoader([
215             FunctionLoader(get_page),
216             orig_loader,
217         ])
218 
219 
220 def main():
221     return SingleUserNotebookApp.launch_instance()
222 
223 
224 if __name__ == "__main__":
225     main()
226
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/jupyterhub/singleuser.py b/jupyterhub/singleuser.py
--- a/jupyterhub/singleuser.py
+++ b/jupyterhub/singleuser.py
@@ -17,7 +17,7 @@
 from tornado import ioloop
 from tornado.web import HTTPError
 
-from traitlets import (
+from IPython.utils.traitlets import (
     Integer,
     Unicode,
     CUnicode,

{"golden_diff": "diff --git a/jupyterhub/singleuser.py b/jupyterhub/singleuser.py\n--- a/jupyterhub/singleuser.py\n+++ b/jupyterhub/singleuser.py\n@@ -17,7 +17,7 @@\n from tornado import ioloop\n from tornado.web import HTTPError\n \n-from traitlets import (\n+from IPython.utils.traitlets import (\n     Integer,\n     Unicode,\n     CUnicode,\n", "issue": "Single user server launch is broken\nI think that #261 broke the launching of the single user server. I am seeing the following errors in the nbgrader tests:\n\n```\nTraceback (most recent call last):\n  File \"/Users/jhamrick/.virtualenvs/nbgrader/bin/jupyterhub-singleuser\", line 6, in <module>\n    exec(compile(open(__file__).read(), __file__, 'exec'))\n  File \"/Users/jhamrick/project/tools/jupyterhub/scripts/jupyterhub-singleuser\", line 4, in <module>\n    main()\n  File \"/Users/jhamrick/project/tools/jupyterhub/jupyterhub/singleuser.py\", line 221, in main\n    return SingleUserNotebookApp.launch_instance()\n  File \"/Users/jhamrick/.virtualenvs/nbgrader/lib/python3.4/site-packages/IPython/config/application.py\", line 573, in launch_instance\n    app.initialize(argv)\n  File \"<string>\", line 2, in initialize\n  File \"/Users/jhamrick/.virtualenvs/nbgrader/lib/python3.4/site-packages/IPython/config/application.py\", line 75, in catch_config_error\n    return method(app, *args, **kwargs)\n  File \"/Users/jhamrick/.virtualenvs/nbgrader/lib/python3.4/site-packages/IPython/html/notebookapp.py\", line 1015, in initialize\n    self.init_webapp()\n  File \"/Users/jhamrick/project/tools/jupyterhub/jupyterhub/singleuser.py\", line 191, in init_webapp\n    s['user'] = self.user\n  File \"/Users/jhamrick/.virtualenvs/nbgrader/lib/python3.4/site-packages/traitlets/traitlets.py\", line 438, in __get__\n    % (self.name, obj))\ntraitlets.traitlets.TraitError: No default value found for None trait of <jupyterhub.singleuser.SingleUserNotebookApp object at 0x102953b00>\n```\n\nIf I revert to the version of jupyterhub prior to that PR, this error does not occur. @epifanio reported on gitter seeing the same thing as well, so I don't think it's isolated to nbgrader.\n\nGiven the error message, I suspect this has to do with ipython/traitlets#39 and/or ipython/traitlets#40 though I haven't actually tested it. I tried giving the `user` trait a default value but it did not seem to fix the error. I will try to do a bit more debugging, but I fear I don't really understand the internals of traitlets well enough to know exactly what's going on here.\n\nPing @takluyver and @minrk ?\n\n", "before_files": [{"content": "#!/usr/bin/env python3\n\"\"\"Extend regular notebook server to be aware of multiuser things.\"\"\"\n\n# Copyright (c) Jupyter Development Team.\n# Distributed under the terms of the Modified BSD License.\n\nimport os\ntry:\n    from urllib.parse import quote\nexcept ImportError:\n    # PY2 Compat\n    from urllib import quote\n\nimport requests\nfrom jinja2 import ChoiceLoader, FunctionLoader\n\nfrom tornado import ioloop\nfrom tornado.web import HTTPError\n\nfrom traitlets import (\n    Integer,\n    Unicode,\n    CUnicode,\n)\n\nfrom IPython.html.notebookapp import NotebookApp, aliases as notebook_aliases\nfrom IPython.html.auth.login import LoginHandler\nfrom IPython.html.auth.logout import LogoutHandler\n\nfrom IPython.html.utils import url_path_join\n\n\nfrom distutils.version import LooseVersion as V\n\nimport IPython\nif V(IPython.__version__) < V('3.0'):\n    raise ImportError(\"JupyterHub Requires IPython >= 3.0, found %s\" % IPython.__version__)\n\n# Define two methods to attach to AuthenticatedHandler,\n# which authenticate via the central auth server.\n\nclass JupyterHubLoginHandler(LoginHandler):\n    @staticmethod\n    def login_available(settings):\n        return True\n    \n    @staticmethod\n    def verify_token(self, cookie_name, encrypted_cookie):\n        \"\"\"method for token verification\"\"\"\n        cookie_cache = self.settings['cookie_cache']\n        if encrypted_cookie in cookie_cache:\n            # we've seen this token before, don't ask upstream again\n            return cookie_cache[encrypted_cookie]\n    \n        hub_api_url = self.settings['hub_api_url']\n        hub_api_key = self.settings['hub_api_key']\n        r = requests.get(url_path_join(\n            hub_api_url, \"authorizations/cookie\", cookie_name, quote(encrypted_cookie, safe=''),\n        ),\n            headers = {'Authorization' : 'token %s' % hub_api_key},\n        )\n        if r.status_code == 404:\n            data = None\n        elif r.status_code == 403:\n            self.log.error(\"I don't have permission to verify cookies, my auth token may have expired: [%i] %s\", r.status_code, r.reason)\n            raise HTTPError(500, \"Permission failure checking authorization, I may need to be restarted\")\n        elif r.status_code >= 500:\n            self.log.error(\"Upstream failure verifying auth token: [%i] %s\", r.status_code, r.reason)\n            raise HTTPError(502, \"Failed to check authorization (upstream problem)\")\n        elif r.status_code >= 400:\n            self.log.warn(\"Failed to check authorization: [%i] %s\", r.status_code, r.reason)\n            raise HTTPError(500, \"Failed to check authorization\")\n        else:\n            data = r.json()\n        cookie_cache[encrypted_cookie] = data\n        return data\n    \n    @staticmethod\n    def get_user(self):\n        \"\"\"alternative get_current_user to query the central server\"\"\"\n        # only allow this to be called once per handler\n        # avoids issues if an error is raised,\n        # since this may be called again when trying to render the error page\n        if hasattr(self, '_cached_user'):\n            return self._cached_user\n        \n        self._cached_user = None\n        my_user = self.settings['user']\n        encrypted_cookie = self.get_cookie(self.cookie_name)\n        if encrypted_cookie:\n            auth_data = JupyterHubLoginHandler.verify_token(self, self.cookie_name, encrypted_cookie)\n            if not auth_data:\n                # treat invalid token the same as no token\n                return None\n            user = auth_data['name']\n            if user == my_user:\n                self._cached_user = user\n                return user\n            else:\n                return None\n        else:\n            self.log.debug(\"No token cookie\")\n            return None\n\n\nclass JupyterHubLogoutHandler(LogoutHandler):\n    def get(self):\n        self.redirect(url_path_join(self.settings['hub_prefix'], 'logout'))\n\n\n# register new hub related command-line aliases\naliases = dict(notebook_aliases)\naliases.update({\n    'user' : 'SingleUserNotebookApp.user',\n    'cookie-name': 'SingleUserNotebookApp.cookie_name',\n    'hub-prefix': 'SingleUserNotebookApp.hub_prefix',\n    'hub-api-url': 'SingleUserNotebookApp.hub_api_url',\n    'base-url': 'SingleUserNotebookApp.base_url',\n})\n\npage_template = \"\"\"\n{% extends \"templates/page.html\" %}\n\n{% block header_buttons %}\n{{super()}}\n\n<a href='{{hub_control_panel_url}}'\n class='btn btn-default btn-sm navbar-btn pull-right'\n style='margin-right: 4px; margin-left: 2px;'\n>\nControl Panel</a>\n{% endblock %}\n\"\"\"\n\nclass SingleUserNotebookApp(NotebookApp):\n    \"\"\"A Subclass of the regular NotebookApp that is aware of the parent multiuser context.\"\"\"\n    user = CUnicode(config=True)\n    def _user_changed(self, name, old, new):\n        self.log.name = new\n    cookie_name = Unicode(config=True)\n    hub_prefix = Unicode(config=True)\n    hub_api_url = Unicode(config=True)\n    aliases = aliases\n    open_browser = False\n    trust_xheaders = True\n    login_handler_class = JupyterHubLoginHandler\n    logout_handler_class = JupyterHubLogoutHandler\n\n    cookie_cache_lifetime = Integer(\n        config=True,\n        default_value=300,\n        allow_none=True,\n        help=\"\"\"\n        Time, in seconds, that we cache a validated cookie before requiring\n        revalidation with the hub.\n        \"\"\",\n    )\n\n    def _log_datefmt_default(self):\n        \"\"\"Exclude date from default date format\"\"\"\n        return \"%Y-%m-%d %H:%M:%S\"\n\n    def _log_format_default(self):\n        \"\"\"override default log format to include time\"\"\"\n        return \"%(color)s[%(levelname)1.1s %(asctime)s.%(msecs).03d %(name)s %(module)s:%(lineno)d]%(end_color)s %(message)s\"\n\n    def _confirm_exit(self):\n        # disable the exit confirmation for background notebook processes\n        ioloop.IOLoop.instance().stop()\n\n    def _clear_cookie_cache(self):\n        self.log.debug(\"Clearing cookie cache\")\n        self.tornado_settings['cookie_cache'].clear()\n    \n    def start(self):\n        # Start a PeriodicCallback to clear cached cookies.  This forces us to\n        # revalidate our user with the Hub at least every\n        # `cookie_cache_lifetime` seconds.\n        if self.cookie_cache_lifetime:\n            ioloop.PeriodicCallback(\n                self._clear_cookie_cache,\n                self.cookie_cache_lifetime * 1e3,\n            ).start()\n        super(SingleUserNotebookApp, self).start()\n    \n    def init_webapp(self):\n        # load the hub related settings into the tornado settings dict\n        env = os.environ\n        s = self.tornado_settings\n        s['cookie_cache'] = {}\n        s['user'] = self.user\n        s['hub_api_key'] = env.pop('JPY_API_TOKEN')\n        s['hub_prefix'] = self.hub_prefix\n        s['cookie_name'] = self.cookie_name\n        s['login_url'] = self.hub_prefix\n        s['hub_api_url'] = self.hub_api_url\n        \n        super(SingleUserNotebookApp, self).init_webapp()\n        self.patch_templates()\n    \n    def patch_templates(self):\n        \"\"\"Patch page templates to add Hub-related buttons\"\"\"\n        env = self.web_app.settings['jinja2_env']\n        \n        env.globals['hub_control_panel_url'] = \\\n            url_path_join(self.hub_prefix, 'home')\n        \n        # patch jinja env loading to modify page template\n        def get_page(name):\n            if name == 'page.html':\n                return page_template\n        \n        orig_loader = env.loader\n        env.loader = ChoiceLoader([\n            FunctionLoader(get_page),\n            orig_loader,\n        ])\n\n\ndef main():\n    return SingleUserNotebookApp.launch_instance()\n\n\nif __name__ == \"__main__\":\n    main()\n", "path": "jupyterhub/singleuser.py"}], "after_files": [{"content": "#!/usr/bin/env python3\n\"\"\"Extend regular notebook server to be aware of multiuser things.\"\"\"\n\n# Copyright (c) Jupyter Development Team.\n# Distributed under the terms of the Modified BSD License.\n\nimport os\ntry:\n    from urllib.parse import quote\nexcept ImportError:\n    # PY2 Compat\n    from urllib import quote\n\nimport requests\nfrom jinja2 import ChoiceLoader, FunctionLoader\n\nfrom tornado import ioloop\nfrom tornado.web import HTTPError\n\nfrom IPython.utils.traitlets import (\n    Integer,\n    Unicode,\n    CUnicode,\n)\n\nfrom IPython.html.notebookapp import NotebookApp, aliases as notebook_aliases\nfrom IPython.html.auth.login import LoginHandler\nfrom IPython.html.auth.logout import LogoutHandler\n\nfrom IPython.html.utils import url_path_join\n\n\nfrom distutils.version import LooseVersion as V\n\nimport IPython\nif V(IPython.__version__) < V('3.0'):\n    raise ImportError(\"JupyterHub Requires IPython >= 3.0, found %s\" % IPython.__version__)\n\n# Define two methods to attach to AuthenticatedHandler,\n# which authenticate via the central auth server.\n\nclass JupyterHubLoginHandler(LoginHandler):\n    @staticmethod\n    def login_available(settings):\n        return True\n    \n    @staticmethod\n    def verify_token(self, cookie_name, encrypted_cookie):\n        \"\"\"method for token verification\"\"\"\n        cookie_cache = self.settings['cookie_cache']\n        if encrypted_cookie in cookie_cache:\n            # we've seen this token before, don't ask upstream again\n            return cookie_cache[encrypted_cookie]\n    \n        hub_api_url = self.settings['hub_api_url']\n        hub_api_key = self.settings['hub_api_key']\n        r = requests.get(url_path_join(\n            hub_api_url, \"authorizations/cookie\", cookie_name, quote(encrypted_cookie, safe=''),\n        ),\n            headers = {'Authorization' : 'token %s' % hub_api_key},\n        )\n        if r.status_code == 404:\n            data = None\n        elif r.status_code == 403:\n            self.log.error(\"I don't have permission to verify cookies, my auth token may have expired: [%i] %s\", r.status_code, r.reason)\n            raise HTTPError(500, \"Permission failure checking authorization, I may need to be restarted\")\n        elif r.status_code >= 500:\n            self.log.error(\"Upstream failure verifying auth token: [%i] %s\", r.status_code, r.reason)\n            raise HTTPError(502, \"Failed to check authorization (upstream problem)\")\n        elif r.status_code >= 400:\n            self.log.warn(\"Failed to check authorization: [%i] %s\", r.status_code, r.reason)\n            raise HTTPError(500, \"Failed to check authorization\")\n        else:\n            data = r.json()\n        cookie_cache[encrypted_cookie] = data\n        return data\n    \n    @staticmethod\n    def get_user(self):\n        \"\"\"alternative get_current_user to query the central server\"\"\"\n        # only allow this to be called once per handler\n        # avoids issues if an error is raised,\n        # since this may be called again when trying to render the error page\n        if hasattr(self, '_cached_user'):\n            return self._cached_user\n        \n        self._cached_user = None\n        my_user = self.settings['user']\n        encrypted_cookie = self.get_cookie(self.cookie_name)\n        if encrypted_cookie:\n            auth_data = JupyterHubLoginHandler.verify_token(self, self.cookie_name, encrypted_cookie)\n            if not auth_data:\n                # treat invalid token the same as no token\n                return None\n            user = auth_data['name']\n            if user == my_user:\n                self._cached_user = user\n                return user\n            else:\n                return None\n        else:\n            self.log.debug(\"No token cookie\")\n            return None\n\n\nclass JupyterHubLogoutHandler(LogoutHandler):\n    def get(self):\n        self.redirect(url_path_join(self.settings['hub_prefix'], 'logout'))\n\n\n# register new hub related command-line aliases\naliases = dict(notebook_aliases)\naliases.update({\n    'user' : 'SingleUserNotebookApp.user',\n    'cookie-name': 'SingleUserNotebookApp.cookie_name',\n    'hub-prefix': 'SingleUserNotebookApp.hub_prefix',\n    'hub-api-url': 'SingleUserNotebookApp.hub_api_url',\n    'base-url': 'SingleUserNotebookApp.base_url',\n})\n\npage_template = \"\"\"\n{% extends \"templates/page.html\" %}\n\n{% block header_buttons %}\n{{super()}}\n\n<a href='{{hub_control_panel_url}}'\n class='btn btn-default btn-sm navbar-btn pull-right'\n style='margin-right: 4px; margin-left: 2px;'\n>\nControl Panel</a>\n{% endblock %}\n\"\"\"\n\nclass SingleUserNotebookApp(NotebookApp):\n    \"\"\"A Subclass of the regular NotebookApp that is aware of the parent multiuser context.\"\"\"\n    user = CUnicode(config=True)\n    def _user_changed(self, name, old, new):\n        self.log.name = new\n    cookie_name = Unicode(config=True)\n    hub_prefix = Unicode(config=True)\n    hub_api_url = Unicode(config=True)\n    aliases = aliases\n    open_browser = False\n    trust_xheaders = True\n    login_handler_class = JupyterHubLoginHandler\n    logout_handler_class = JupyterHubLogoutHandler\n\n    cookie_cache_lifetime = Integer(\n        config=True,\n        default_value=300,\n        allow_none=True,\n        help=\"\"\"\n        Time, in seconds, that we cache a validated cookie before requiring\n        revalidation with the hub.\n        \"\"\",\n    )\n\n    def _log_datefmt_default(self):\n        \"\"\"Exclude date from default date format\"\"\"\n        return \"%Y-%m-%d %H:%M:%S\"\n\n    def _log_format_default(self):\n        \"\"\"override default log format to include time\"\"\"\n        return \"%(color)s[%(levelname)1.1s %(asctime)s.%(msecs).03d %(name)s %(module)s:%(lineno)d]%(end_color)s %(message)s\"\n\n    def _confirm_exit(self):\n        # disable the exit confirmation for background notebook processes\n        ioloop.IOLoop.instance().stop()\n\n    def _clear_cookie_cache(self):\n        self.log.debug(\"Clearing cookie cache\")\n        self.tornado_settings['cookie_cache'].clear()\n    \n    def start(self):\n        # Start a PeriodicCallback to clear cached cookies.  This forces us to\n        # revalidate our user with the Hub at least every\n        # `cookie_cache_lifetime` seconds.\n        if self.cookie_cache_lifetime:\n            ioloop.PeriodicCallback(\n                self._clear_cookie_cache,\n                self.cookie_cache_lifetime * 1e3,\n            ).start()\n        super(SingleUserNotebookApp, self).start()\n    \n    def init_webapp(self):\n        # load the hub related settings into the tornado settings dict\n        env = os.environ\n        s = self.tornado_settings\n        s['cookie_cache'] = {}\n        s['user'] = self.user\n        s['hub_api_key'] = env.pop('JPY_API_TOKEN')\n        s['hub_prefix'] = self.hub_prefix\n        s['cookie_name'] = self.cookie_name\n        s['login_url'] = self.hub_prefix\n        s['hub_api_url'] = self.hub_api_url\n        \n        super(SingleUserNotebookApp, self).init_webapp()\n        self.patch_templates()\n    \n    def patch_templates(self):\n        \"\"\"Patch page templates to add Hub-related buttons\"\"\"\n        env = self.web_app.settings['jinja2_env']\n        \n        env.globals['hub_control_panel_url'] = \\\n            url_path_join(self.hub_prefix, 'home')\n        \n        # patch jinja env loading to modify page template\n        def get_page(name):\n            if name == 'page.html':\n                return page_template\n        \n        orig_loader = env.loader\n        env.loader = ChoiceLoader([\n            FunctionLoader(get_page),\n            orig_loader,\n        ])\n\n\ndef main():\n    return SingleUserNotebookApp.launch_instance()\n\n\nif __name__ == \"__main__\":\n    main()\n", "path": "jupyterhub/singleuser.py"}]}