diff --git a/jupyterhub/apihandlers/users.py b/jupyterhub/apihandlers/users.py index b1a1a157..d872ff23 100644 --- a/jupyterhub/apihandlers/users.py +++ b/jupyterhub/apihandlers/users.py @@ -8,7 +8,7 @@ import json from tornado import gen, web from .. import orm -from ..utils import admin_only, awaitable +from ..utils import admin_only, maybe_future from .base import APIHandler @@ -76,7 +76,7 @@ class UserListAPIHandler(APIHandler): user.admin = True self.db.commit() try: - await awaitable(self.authenticator.add_user(user)) + await maybe_future(self.authenticator.add_user(user)) except Exception as e: self.log.error("Failed to create user: %s" % name, exc_info=True) self.users.delete(user) @@ -125,7 +125,7 @@ class UserAPIHandler(APIHandler): self.db.commit() try: - await awaitable(self.authenticator.add_user(user)) + await maybe_future(self.authenticator.add_user(user)) except Exception: self.log.error("Failed to create user: %s" % name, exc_info=True) # remove from registry @@ -149,7 +149,7 @@ class UserAPIHandler(APIHandler): if user.spawner._stop_pending: raise web.HTTPError(400, "%s's server is in the process of stopping, please wait." % name) - await awaitable(self.authenticator.delete_user(user)) + await maybe_future(self.authenticator.delete_user(user)) # remove from registry self.users.delete(user) diff --git a/jupyterhub/app.py b/jupyterhub/app.py index ebe5e458..7f5521d5 100644 --- a/jupyterhub/app.py +++ b/jupyterhub/app.py @@ -57,7 +57,7 @@ from .log import CoroutineLogFormatter, log_request from .proxy import Proxy, ConfigurableHTTPProxy from .traitlets import URLPrefix, Command from .utils import ( - awaitable, + maybe_future, url_path_join, ISO8601_ms, ISO8601_s, print_stacks, print_ps_info, @@ -1052,7 +1052,7 @@ class JupyterHub(Application): # and persist across sessions. for user in db.query(orm.User): try: - await awaitable(self.authenticator.add_user(user)) + await maybe_future(self.authenticator.add_user(user)) except Exception: self.log.exception("Error adding user %s already in db", user.name) if self.authenticator.delete_invalid_users: @@ -1083,7 +1083,7 @@ class JupyterHub(Application): db.add(group) for username in usernames: username = self.authenticator.normalize_username(username) - if not (await awaitable(self.authenticator.check_whitelist(username))): + if not (await maybe_future(self.authenticator.check_whitelist(username))): raise ValueError("Username %r is not in whitelist" % username) user = orm.User.find(db, name=username) if user is None: @@ -1107,7 +1107,7 @@ class JupyterHub(Application): for token, name in token_dict.items(): if kind == 'user': name = self.authenticator.normalize_username(name) - if not (await awaitable(self.authenticator.check_whitelist(name))): + if not (await maybe_future(self.authenticator.check_whitelist(name))): raise ValueError("Token name %r is not in whitelist" % name) if not self.authenticator.validate_username(name): raise ValueError("Token name %r is not valid" % name) @@ -1497,7 +1497,7 @@ class JupyterHub(Application): # clean up proxy while single-user servers are shutting down if self.cleanup_proxy: if self.proxy.should_start: - await awaitable(self.proxy.stop()) + await maybe_future(self.proxy.stop()) else: self.log.info("I didn't start the proxy, I can't clean it up") else: diff --git a/jupyterhub/auth.py b/jupyterhub/auth.py index adce72db..03cbc515 100644 --- a/jupyterhub/auth.py +++ b/jupyterhub/auth.py @@ -23,7 +23,7 @@ from traitlets.config import LoggingConfigurable from traitlets import Bool, Set, Unicode, Dict, Any, default, observe from .handlers.login import LoginHandler -from .utils import awaitable, url_path_join +from .utils import maybe_future, url_path_join from .traitlets import Command @@ -244,7 +244,7 @@ class Authenticator(LoggingConfigurable): self.log.warning("Disallowing invalid username %r.", username) return - whitelist_pass = await awaitable(self.check_whitelist(username)) + whitelist_pass = await maybe_future(self.check_whitelist(username)) if whitelist_pass: return authenticated else: @@ -481,14 +481,14 @@ class LocalAuthenticator(Authenticator): If self.create_system_users, the user will attempt to be created if it doesn't exist. """ - user_exists = await awaitable(self.system_user_exists(user)) + user_exists = await maybe_future(self.system_user_exists(user)) if not user_exists: if self.create_system_users: - await awaitable(self.add_system_user(user)) + await maybe_future(self.add_system_user(user)) else: raise KeyError("User %s does not exist." % user.name) - await awaitable(super().add_user(user)) + await maybe_future(super().add_user(user)) @staticmethod def system_user_exists(user): diff --git a/jupyterhub/crypto.py b/jupyterhub/crypto.py index 6e9a9a40..bffb7b0a 100644 --- a/jupyterhub/crypto.py +++ b/jupyterhub/crypto.py @@ -19,7 +19,7 @@ except ImportError: class InvalidToken(Exception): pass -from .utils import awaitable +from .utils import maybe_future KEY_ENV = 'JUPYTERHUB_CRYPT_KEY' @@ -133,7 +133,7 @@ class CryptKeeper(SingletonConfigurable): def encrypt(self, data): """Encrypt an object with cryptography""" self.check_available() - return awaitable(self.executor.submit(self._encrypt, data)) + return maybe_future(self.executor.submit(self._encrypt, data)) def _decrypt(self, encrypted): decrypted = self.fernet.decrypt(encrypted) @@ -142,7 +142,7 @@ class CryptKeeper(SingletonConfigurable): def decrypt(self, encrypted): """Decrypt an object with cryptography""" self.check_available() - return awaitable(self.executor.submit(self._decrypt, encrypted)) + return maybe_future(self.executor.submit(self._decrypt, encrypted)) def encrypt(data): diff --git a/jupyterhub/handlers/base.py b/jupyterhub/handlers/base.py index 8ca99cb0..41279206 100644 --- a/jupyterhub/handlers/base.py +++ b/jupyterhub/handlers/base.py @@ -23,7 +23,7 @@ from .. import __version__ from .. import orm from ..objects import Server from ..spawner import LocalProcessSpawner -from ..utils import awaitable, url_path_join +from ..utils import maybe_future, url_path_join from ..metrics import ( SERVER_SPAWN_DURATION_SECONDS, ServerSpawnStatus, PROXY_ADD_DURATION_SECONDS, ProxyAddStatus @@ -387,7 +387,7 @@ class BaseHandler(RequestHandler): self.set_hub_cookie(user) def authenticate(self, data): - return awaitable(self.authenticator.get_authenticated_user(self, data)) + return maybe_future(self.authenticator.get_authenticated_user(self, data)) def get_next_url(self, user=None): """Get the next_url for login redirect @@ -421,7 +421,7 @@ class BaseHandler(RequestHandler): new_user = username not in self.users user = self.user_from_username(username) if new_user: - await awaitable(self.authenticator.add_user(user)) + await maybe_future(self.authenticator.add_user(user)) # Only set `admin` if the authenticator returned an explicit value. if admin is not None and admin != user.admin: user.admin = admin @@ -577,7 +577,7 @@ class BaseHandler(RequestHandler): # hook up spawner._spawn_future so that other requests can await # this result - finish_spawn_future = spawner._spawn_future = awaitable(finish_user_spawn()) + finish_spawn_future = spawner._spawn_future = maybe_future(finish_user_spawn()) def _clear_spawn_future(f): # clear spawner._spawn_future when it's done # keep an exception around, though, to prevent repeated implicit spawns diff --git a/jupyterhub/spawner.py b/jupyterhub/spawner.py index bf8b3674..2086bc3f 100644 --- a/jupyterhub/spawner.py +++ b/jupyterhub/spawner.py @@ -28,7 +28,7 @@ from traitlets import ( from .objects import Server from .traitlets import Command, ByteSpecification, Callable -from .utils import awaitable, random_port, url_path_join, exponential_backoff +from .utils import maybe_future, random_port, url_path_join, exponential_backoff class Spawner(LoggingConfigurable): @@ -269,7 +269,7 @@ class Spawner(LoggingConfigurable): Introduced. """ if callable(self.options_form): - options_form = await awaitable(self.options_form(self)) + options_form = await maybe_future(self.options_form(self)) else: options_form = self.options_form @@ -783,7 +783,7 @@ class Spawner(LoggingConfigurable): for callback in callbacks: try: - await awaitable(callback()) + await maybe_future(callback()) except Exception: self.log.exception("Unhandled error in poll callback for %s", self) return status diff --git a/jupyterhub/user.py b/jupyterhub/user.py index 13a39604..6041ba9f 100644 --- a/jupyterhub/user.py +++ b/jupyterhub/user.py @@ -12,7 +12,7 @@ from tornado import gen from tornado.log import app_log from traitlets import HasTraits, Any, Dict, default -from .utils import awaitable, url_path_join +from .utils import maybe_future, url_path_join from . import orm from ._version import _check_version, __version__ @@ -378,14 +378,14 @@ class User: # trigger pre-spawn hook on authenticator authenticator = self.authenticator if (authenticator): - await awaitable(authenticator.pre_spawn_start(self, spawner)) + await maybe_future(authenticator.pre_spawn_start(self, spawner)) spawner._start_pending = True # wait for spawner.start to return try: # run optional preparation work to bootstrap the notebook - await awaitable(spawner.run_pre_spawn_hook()) - f = awaitable(spawner.start()) + await maybe_future(spawner.run_pre_spawn_hook()) + f = maybe_future(spawner.start()) # commit any changes in spawner.start (always commit db changes before yield) db.commit() ip_port = await gen.with_timeout(timedelta(seconds=spawner.start_timeout), f) @@ -533,7 +533,7 @@ class User: auth = spawner.authenticator try: if auth: - await awaitable( + await maybe_future( auth.post_spawn_stop(self, spawner) ) except Exception: diff --git a/jupyterhub/utils.py b/jupyterhub/utils.py index 19184297..21cb7ab6 100644 --- a/jupyterhub/utils.py +++ b/jupyterhub/utils.py @@ -123,7 +123,7 @@ async def exponential_backoff( deadline = random.uniform(deadline - tol, deadline + tol) scale = 1 while True: - ret = await awaitable(pass_func(*args, **kwargs)) + ret = await maybe_future(pass_func(*args, **kwargs)) # Truthy! if ret: return ret @@ -414,16 +414,17 @@ def print_stacks(file=sys.stderr): task.print_stack(file=file) -def awaitable(obj): - """Wrap an object in something that's awaitable +def maybe_future(obj): + """Return an asyncio Future Use instead of gen.maybe_future For our compatibility, this must accept: - - asyncio coroutine (gen.maybe_future doesn't work) + - asyncio coroutine (gen.maybe_future doesn't work in tornado < 5) - tornado coroutine (asyncio.ensure_future doesn't work) - scalar (asyncio.ensure_future doesn't work) + - concurrent.futures.Future (asyncio.ensure_future doesn't work) - tornado Future (works both ways) - asyncio Future (works both ways) """