diff --git a/.travis.yml b/.travis.yml index a33d822147..512291c05d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -83,6 +83,10 @@ matrix: dist: xenial # required for Python >= 3.7 (travis-ci/travis-ci#9069) - python: 3.6 env: GROUP=docs + - python: 3.6 # extra test for older tornados + env: + - GROUP=python + - EXTRA_PIP="tornado<5" after_success: - codecov diff --git a/jupyter_server/utils.py b/jupyter_server/utils.py index 5d98154767..c5b4b9b07f 100644 --- a/jupyter_server/utils.py +++ b/jupyter_server/utils.py @@ -17,8 +17,23 @@ try: - from urllib.parse import quote, unquote, urlparse, urljoin + from inspect import isawaitable +except ImportError: + def isawaitable(f): + """If isawaitable is undefined, nothing is awaitable""" + return False + +try: + from concurrent.futures import Future as ConcurrentFuture +except ImportError: + class ConcurrentFuture: + """If concurrent.futures isn't importable, nothing will be a c.f.Future""" + pass + +try: + from urllib.parse import quote, unquote, urlparse from urllib.request import pathname2url + except ImportError: from urllib import quote, unquote, pathname2url from urlparse import urlparse, urljoin @@ -317,18 +332,29 @@ def _check_pid_posix(pid): def maybe_future(obj): - """Like tornado's deprecated gen.maybe_future - + """Like tornado's gen.maybe_future but more compatible with asyncio for recent versions of tornado """ - if inspect.isawaitable(obj): + if isinstance(obj, TornadoFuture): + return obj + elif isawaitable(obj): return asyncio.ensure_future(obj) - elif isinstance(obj, concurrent.futures.Future): + elif isinstance(obj, ConcurrentFuture): return asyncio.wrap_future(obj) else: # not awaitable, wrap scalar in future - f = asyncio.Future() + f = TornadoFuture() f.set_result(obj) return f +# monkeypatch tornado gen.maybe_future +# on Python 3 +# TODO: remove monkeypatch after backporting smaller fix to 5.x +try: + import asyncio +except ImportError: + pass +else: + import tornado.gen + tornado.gen.maybe_future = maybe_future