diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index c27ae784ea8..9d6b6a526c4 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -10,11 +10,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ["3.6", "3.7", "3.8", "3.9"] - exclude: - # Several failures; Python 3.6 to be phased out soon - - os: macos-latest - python-version: "3.6" + python-version: ["3.7", "3.8", "3.9"] # Uncomment to stress-test the test suite for random failures # This will take a LONG time and delay all PRs across the whole github.com/dask! @@ -23,6 +19,8 @@ jobs: steps: - name: Checkout source uses: actions/checkout@v2 + with: + fetch-depth: 0 - name: Setup Conda Environment uses: conda-incubator/setup-miniconda@v2 diff --git a/continuous_integration/environment-3.6.yaml b/continuous_integration/environment-3.6.yaml deleted file mode 100644 index 7a13f63b232..00000000000 --- a/continuous_integration/environment-3.6.yaml +++ /dev/null @@ -1,47 +0,0 @@ -name: dask-distributed -channels: - - conda-forge - - defaults -dependencies: - - python=3.6 - - pip - - asyncssh - - bokeh - - click - - cloudpickle - - contextvars # Backport from Python 3.7 - - dask # overridden by git tip below - - filesystem-spec - - h5py - - ipykernel - - ipywidgets - - joblib - - jupyter_client - - msgpack-python - - netcdf4 - - paramiko - - pickle5 # Backport from Python 3.8 - - prometheus_client - - psutil - - pytest - - pytest-asyncio<0.14.0 - - pytest-faulthandler - - pytest-repeat - - pytest-rerunfailures - - pytest-timeout - - requests - - s3fs - - scikit-learn - - scipy - - sortedcollections - - tblib - - toolz - - tornado=5 # Only tested here - - zict - - zstandard - - pip: - # The '.git@master' is a hack needed to work around the failure - # ERROR: Package 'dask' requires a different Python: 3.6.12 not in '>=3.6' - # It only happens in CI; cannot reproduce locally - - git+https://github.com/dask/dask.git@master - - keras diff --git a/continuous_integration/environment-3.7.yaml b/continuous_integration/environment-3.7.yaml index 7057155b470..bb7838fe205 100644 --- a/continuous_integration/environment-3.7.yaml +++ b/continuous_integration/environment-3.7.yaml @@ -36,7 +36,7 @@ dependencies: - sortedcollections - tblib - toolz - - tornado=6 + - tornado=5 # Only tested here - zict - zstandard - pip: diff --git a/distributed/client.py b/distributed/client.py index fa17a6b710d..4f1f117f3c5 100644 --- a/distributed/client.py +++ b/distributed/client.py @@ -756,15 +756,6 @@ def as_current(self): method to return self. Any Future objects deserialized inside this context manager will be automatically attached to this Client. """ - # In Python 3.6, contextvars are thread-local but not Task-local. - # We can still detect a race condition though. - if sys.version_info < (3, 7) and _current_client.get() not in (self, None): - raise RuntimeError( - "Detected race condition where multiple asynchronous clients tried " - "entering the as_current() context manager at the same time. " - "Please upgrade to Python 3.7+." - ) - tok = _current_client.set(self) try: yield diff --git a/distributed/comm/tests/test_comms.py b/distributed/comm/tests/test_comms.py index b7a2061af48..750c30d5c27 100644 --- a/distributed/comm/tests/test_comms.py +++ b/distributed/comm/tests/test_comms.py @@ -223,8 +223,7 @@ async def handle_comm(comm): assert not handle_comm_called writer.close() - if hasattr(writer, "wait_closed"): # always true for python >= 3.7, but not for 3.6 - await writer.wait_closed() + await writer.wait_closed() @pytest.mark.asyncio diff --git a/distributed/compatibility.py b/distributed/compatibility.py index 6141f3559a7..982b7017951 100644 --- a/distributed/compatibility.py +++ b/distributed/compatibility.py @@ -11,16 +11,3 @@ MACOS = sys.platform == "darwin" WINDOWS = sys.platform.startswith("win") TORNADO6 = tornado.version_info[0] >= 6 -PY37 = sys.version_info[:2] >= (3, 7) - -if sys.version_info[:2] >= (3, 7): - from asyncio import get_running_loop -else: - - def get_running_loop(): - from asyncio import _get_running_loop - - loop = _get_running_loop() - if loop is None: - raise RuntimeError("no running event loop") - return loop diff --git a/distributed/deploy/tests/test_local.py b/distributed/deploy/tests/test_local.py index 5a35f36695c..8eaea2ba83f 100644 --- a/distributed/deploy/tests/test_local.py +++ b/distributed/deploy/tests/test_local.py @@ -1020,9 +1020,6 @@ async def test_capture_security(cleanup, temporary): @pytest.mark.asyncio -@pytest.mark.skipif( - sys.version_info < (3, 7), reason="asyncio.all_tasks not implemented" -) async def test_no_danglng_asyncio_tasks(cleanup): start = asyncio.all_tasks() async with LocalCluster(asynchronous=True, processes=False): diff --git a/distributed/metrics.py b/distributed/metrics.py index 0f7d78a8129..163a982b792 100755 --- a/distributed/metrics.py +++ b/distributed/metrics.py @@ -84,60 +84,13 @@ def resync(self): # Under modern Unices, time.time() should be good enough time = timemod.time - -def _native_thread_time(): - # Python 3.7+, not all platforms - return timemod.thread_time() - - -def _linux_thread_time(): - # Use hardcoded CLOCK_THREAD_CPUTIME_ID on Python 3 <= 3.6 - if sys.platform != "linux": - raise OSError - return timemod.clock_gettime(3) - - -def _native_process_time(): - # Python 3, should work everywhere - return timemod.process_time() - - -def _native_clock_func(): - # time.clock() unfortunately has different semantics depending on the - # platform. On POSIX it's a per-process CPU timer (with possibly - # poor resolution). On Windows it's a high-resolution wall clock timer. - return timemod.clock() - - -def _detect_process_time(): - """ - Return a per-process CPU timer function if possible, otherwise - a wall-clock timer. - """ - for func in [_native_process_time]: - try: - func() - return func - except (AttributeError, OSError): - pass - # Only Python 2? - return _native_clock_func - - -def _detect_thread_time(): - """ - Return a per-thread CPU timer function if possible, otherwise - a per-process CPU timer function, or at worse a wall-clock timer. - """ - for func in [_native_thread_time, _linux_thread_time, _native_process_time]: - try: - func() - return func - except (AttributeError, OSError): - pass - # Only Python 2? - return time - - -process_time = _detect_process_time() -thread_time = _detect_thread_time() +process_time = timemod.process_time + +# Get a per-thread CPU timer function if possible, otherwise +# use a per-process CPU timer function. +try: + # thread_time is supported on Python 3.7+ but not all platforms + thread_time = timemod.thread_time +except (AttributeError, OSError): + # process_time is supported on Python 3.3+ everywhere + thread_time = process_time diff --git a/distributed/pubsub.py b/distributed/pubsub.py index 3b580d6180a..91200be06eb 100644 --- a/distributed/pubsub.py +++ b/distributed/pubsub.py @@ -1,6 +1,5 @@ import asyncio from collections import defaultdict, deque -from contextlib import suppress import logging import threading import weakref @@ -421,8 +420,7 @@ async def _(): try: await asyncio.wait_for(_(), timeout2) finally: - with suppress(RuntimeError): # Python 3.6 fails here sometimes - self.condition.release() + self.condition.release() return self.buffer.popleft() diff --git a/distributed/tests/test_client.py b/distributed/tests/test_client.py index 100b52eaf81..9caa3ccb08f 100644 --- a/distributed/tests/test_client.py +++ b/distributed/tests/test_client.py @@ -3668,7 +3668,6 @@ async def test_reconnect_timeout(c, s): @pytest.mark.avoid_ci(reason="hangs on github actions ubuntu-latest CI") @pytest.mark.slow @pytest.mark.skipif(WINDOWS, reason="num_fds not supported on windows") -@pytest.mark.skipif(sys.version_info < (3, 7), reason="TODO: intermittent failures") @pytest.mark.parametrize("worker,count,repeat", [(Worker, 100, 5), (Nanny, 10, 20)]) def test_open_close_many_workers(loop, worker, count, repeat): psutil = pytest.importorskip("psutil") @@ -4054,10 +4053,6 @@ def run2(): t2.join() -@pytest.mark.xfail( - sys.version_info < (3, 7), - reason="Python 3.6 contextvars are not copied on Task creation", -) @gen_cluster(client=False) async def test_as_current_is_task_local(s, a, b): l1 = asyncio.Lock() diff --git a/distributed/tests/test_core.py b/distributed/tests/test_core.py index e9b77c0b1a3..071b6b039c7 100644 --- a/distributed/tests/test_core.py +++ b/distributed/tests/test_core.py @@ -1,7 +1,6 @@ import asyncio import os import socket -import sys import threading import weakref import warnings @@ -180,10 +179,6 @@ class MyServer(Server): default_port = 8756 -@pytest.mark.skipif( - sys.version_info < (3, 7), - reason="asynccontextmanager not avaiable before Python 3.7", -) @pytest.mark.asyncio async def test_server_listen(): """ diff --git a/distributed/tests/test_diskutils.py b/distributed/tests/test_diskutils.py index f4afb6d66d4..3b97fde1498 100644 --- a/distributed/tests/test_diskutils.py +++ b/distributed/tests/test_diskutils.py @@ -277,8 +277,6 @@ def _test_workspace_concurrency(tmpdir, timeout, max_procs): def test_workspace_concurrency(tmpdir): if WINDOWS: raise pytest.xfail.Exception("TODO: unknown failure on windows") - if sys.version_info < (3, 7): - raise pytest.xfail.Exception("TODO: unknown failure on Python 3.6") _test_workspace_concurrency(tmpdir, 5.0, 6) diff --git a/distributed/tests/test_scheduler.py b/distributed/tests/test_scheduler.py index 0d4cad348c2..701a874fbcc 100644 --- a/distributed/tests/test_scheduler.py +++ b/distributed/tests/test_scheduler.py @@ -1818,9 +1818,6 @@ async def test_get_task_duration(c, s, a, b): @pytest.mark.asyncio -@pytest.mark.skipif( - sys.version_info < (3, 7), reason="asyncio.all_tasks not implemented" -) async def test_no_danglng_asyncio_tasks(cleanup): start = asyncio.all_tasks() async with Scheduler(port=0) as s: diff --git a/distributed/utils.py b/distributed/utils.py index dd6ca2fad02..115e9578b6b 100644 --- a/distributed/utils.py +++ b/distributed/utils.py @@ -55,7 +55,7 @@ except ImportError: PollIOLoop = None # dropped in tornado 6.0 -from .compatibility import PYPY, WINDOWS, get_running_loop +from .compatibility import PYPY, WINDOWS from .metrics import time @@ -1165,7 +1165,7 @@ def reset_logger_locks(): if is_kernel(): try: - get_running_loop() + asyncio.get_running_loop() except RuntimeError: is_kernel_and_no_running_loop = True diff --git a/distributed/variable.py b/distributed/variable.py index c3fdc94d0d7..83bea566b45 100644 --- a/distributed/variable.py +++ b/distributed/variable.py @@ -89,8 +89,7 @@ async def _(): # Python 3.6 is odd and requires special help here await asyncio.wait_for(_(), timeout=left) finally: - with suppress(RuntimeError): # Python 3.6 loses lock on finally clause - self.started.release() + self.started.release() record = self.variables[name] if record["type"] == "Future": diff --git a/docs/source/install.rst b/docs/source/install.rst index db1bf316400..695cd3e777f 100644 --- a/docs/source/install.rst +++ b/docs/source/install.rst @@ -38,6 +38,6 @@ Notes `_. with Python from macports that makes executables be placed in a location that is not available by default. A simple solution is to extend the ``PATH`` environment variable to the location -where Python from macports install the binaries. For example, for Python 3.6:: +where Python from macports install the binaries. For example, for Python 3.7:: - $ export PATH=/opt/local/Library/Frameworks/Python.framework/Versions/3.6/bin:$PATH + $ export PATH=/opt/local/Library/Frameworks/Python.framework/Versions/3.7/bin:$PATH diff --git a/requirements.txt b/requirements.txt index 37d23c45e69..fd8159cfefe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ click >= 6.6 cloudpickle >= 1.5.0 -contextvars;python_version<'3.7' dask >= 2021.03.0 msgpack >= 0.6.0 psutil >= 5.0 diff --git a/setup.py b/setup.py index 5483e0b963d..8aaaeee5de8 100755 --- a/setup.py +++ b/setup.py @@ -67,7 +67,7 @@ url="https://distributed.dask.org", maintainer="Matthew Rocklin", maintainer_email="mrocklin@gmail.com", - python_requires=">=3.6", + python_requires=">=3.7", license="BSD", package_data={ "": ["templates/index.html", "template.html"], @@ -89,7 +89,6 @@ "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9",