From 67108c91d2abb445655cc2af446fdb52ca7890c4 Mon Sep 17 00:00:00 2001 From: Oliver Berger Date: Wed, 26 Sep 2018 10:18:20 +0200 Subject: [PATCH] use asyncio.current_task in python 3.7 (#24) Also: - randomized tests - fixed dependent tests - updated ci requirements - added tox.ini --- .travis.yml | 14 +++++++++----- Makefile | 2 +- aiotask_context/__init__.py | 33 +++++++++++++++++++++++++-------- requirements-ci.txt | 11 ++++++----- tests/conftest.py | 12 +++++++++--- tests/test_acceptance.py | 3 ++- tox.ini | 35 +++++++++++++++++++++++++++++++++++ 7 files changed, 87 insertions(+), 23 deletions(-) create mode 100644 tox.ini diff --git a/.travis.yml b/.travis.yml index 9342274..0a7b916 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,10 +4,14 @@ python: - "3.5" - "3.6" -install: - - pip install -r requirements-ci.txt +matrix: + include: + - python: 3.7 + dist: xenial + sudo: true -script: +install: pip install tox-travis flake8 + +script: tox - make syntax - - python setup.py develop - - make test + - tox diff --git a/Makefile b/Makefile index f7799df..900a008 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ syntax: - flake8 --exclude=examples/ + flake8 --exclude=examples/,.tox,.eggs cov: pytest --cov-report term-missing --cov=aiotask_context -sv tests/test_context.py diff --git a/aiotask_context/__init__.py b/aiotask_context/__init__.py index 8265f58..6ad8013 100644 --- a/aiotask_context/__init__.py +++ b/aiotask_context/__init__.py @@ -1,8 +1,22 @@ import asyncio import logging +import sys from collections import ChainMap -from functools import partial from copy import deepcopy +from functools import partial + +PY37 = sys.version_info >= (3, 7) + +if PY37: + def asyncio_current_task(loop=None): + """Return the current task or None.""" + try: + return asyncio.current_task(loop) + except RuntimeError: + # simulate old behaviour + return None +else: + asyncio_current_task = asyncio.Task.current_task logger = logging.getLogger(__name__) @@ -54,7 +68,7 @@ def task_factory(loop, coro, copy_context=False, context_factory=None): del task._source_traceback[-1] try: - context = asyncio.Task.current_task(loop=loop).context + context = asyncio_current_task(loop=loop).context except AttributeError: context = None @@ -95,10 +109,11 @@ def get(key, default=None): :param default: None by default, returned in case key is not found. :return: Value stored inside the dict[key]. """ - if not asyncio.Task.current_task(): + current_task = asyncio_current_task() + if not current_task: raise ValueError(NO_LOOP_EXCEPTION_MSG.format(key)) - return asyncio.Task.current_task().context.get(key, default) + return current_task.context.get(key, default) def set(key, value): @@ -109,10 +124,11 @@ def set(key, value): :param value: value to store inside context[key]. :raises """ - if not asyncio.Task.current_task(): + current_task = asyncio_current_task() + if not current_task: raise ValueError(NO_LOOP_EXCEPTION_MSG.format(key)) - asyncio.Task.current_task().context[key] = value + current_task.context[key] = value def clear(): @@ -121,7 +137,8 @@ def clear(): :raises ValueError: if no current task. """ - if not asyncio.Task.current_task(): + current_task = asyncio_current_task() + if not current_task: raise ValueError("No event loop found") - asyncio.Task.current_task().context.clear() + current_task.context.clear() diff --git a/requirements-ci.txt b/requirements-ci.txt index c8caa48..72f3476 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -1,5 +1,6 @@ -pytest==3.0.1 -pytest-asyncio==0.4.1 -pytest-cov==2.3.1 -flake8==3.0.4 -uvloop==0.8.0 +pytest==3.8.0 +pytest-randomly==1.2.3 +pytest-asyncio==0.9.0 +pytest-cov==2.6.0 +flake8==3.5.0 +uvloop==0.11.2 diff --git a/tests/conftest.py b/tests/conftest.py index f50c9aa..f6934d3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,16 +8,22 @@ @pytest.fixture() def asyncio_loop(): - loop = asyncio.get_event_loop_policy().new_event_loop() + asyncio.set_event_loop_policy(None) + loop = asyncio.get_event_loop() yield loop loop.close() + # restore the virgin state + asyncio.set_event_loop_policy(None) @pytest.fixture() def uvloop_loop(): - loop = uvloop.new_event_loop() + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + loop = asyncio.get_event_loop() yield loop loop.close() + # restore the virgin state + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) @pytest.fixture(params=[ @@ -25,7 +31,7 @@ def uvloop_loop(): 'uvloop_loop' ]) def event_loop(request): - return request.getfuncargvalue(request.param) + return request.getfixturevalue(request.param) @pytest.fixture(autouse=True) diff --git a/tests/test_acceptance.py b/tests/test_acceptance.py index 97047f4..6051c74 100644 --- a/tests/test_acceptance.py +++ b/tests/test_acceptance.py @@ -28,7 +28,8 @@ def dummy2(a, b): def dummy1(n_tasks): context.set("key", str(uuid.uuid4())) tasks = [ - asyncio.ensure_future(dummy2(id(asyncio.Task.current_task()), n)) for n in range(n_tasks)] + asyncio.ensure_future( + dummy2(id(context.asyncio_current_task()), n)) for n in range(n_tasks)] results = yield from asyncio.gather(*tasks) info = defaultdict(list) for taskid, n, key in results: diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..93fcbbb --- /dev/null +++ b/tox.ini @@ -0,0 +1,35 @@ +[tox] +skipsdist = True +envlist = + setup + py35 + py36 + py37 + report + +[testenv:setup] +deps = coverage +setenv = + COVERAGE_FILE = .coverage +commands = + coverage erase + +[testenv] +setenv = + COVERAGE_FILE = .coverage.{envname} +deps = + coverage + pytest +commands = + pip install -e . + pip install -r requirements-ci.txt + coverage run --source aiotask_context -m pytest + +[testenv:report] +deps = coverage +setenv = + COVERAGE_FILE = .coverage +commands = + coverage combine + coverage report -m + coverage xml