Repository: joblib/joblib
Pull request #1253 (2022-02-02 16:32:38)

Drop 3.6 support
Loky is about to do a big spring cleanup  to remove all hard to maintain backports for Python versions in the range [2.7-3.6].

Furthermore we have failing tests with joblib on Python 3.6. Let's not waste scarce maintainer's time on no longer supported Python versions.

codecov[bot]: # [Codecov](https://codecov.io/gh/joblib/joblib/pull/1253?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib) Report
> Merging [#1253](https://codecov.io/gh/joblib/joblib/pull/1253?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib) (3b39ce9) into [master](https://codecov.io/gh/joblib/joblib/commit/7742f5882273889f7aaf1d483a8a1c72a97d57e3?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib) (7742f58) will **decrease** coverage by `8.74%`.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/joblib/joblib/pull/1253/graphs/tree.svg?width=650&height=150&src=pr&token=gA6LF5DGTW&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib)](https://codecov.io/gh/joblib/joblib/pull/1253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib)

```diff
@@            Coverage Diff             @@
##           master    #1253      +/-   ##
==========================================
- Coverage   88.43%   79.69%   -8.75%     
==========================================
  Files          47       47              
  Lines        7082     6874     -208     
==========================================
- Hits         6263     5478     -785     
- Misses        819     1396     +577     
```


| [Impacted Files](https://codecov.io/gh/joblib/joblib/pull/1253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib) | Coverage Δ | |
|---|---|---|
| [joblib/test/test\_deprecated\_objects.py](https://codecov.io/gh/joblib/joblib/pull/1253/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib#diff-am9ibGliL3Rlc3QvdGVzdF9kZXByZWNhdGVkX29iamVjdHMucHk=) | `100.00% <ø> (ø)` | |
| [joblib/test/test\_memmapping.py](https://codecov.io/gh/joblib/joblib/pull/1253/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib#diff-am9ibGliL3Rlc3QvdGVzdF9tZW1tYXBwaW5nLnB5) | `37.07% <0.00%> (-62.17%)` | :arrow_down: |
| [joblib/backports.py](https://codecov.io/gh/joblib/joblib/pull/1253/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib#diff-am9ibGliL2JhY2twb3J0cy5weQ==) | `44.73% <0.00%> (-39.48%)` | :arrow_down: |
| [joblib/\_memmapping\_reducer.py](https://codecov.io/gh/joblib/joblib/pull/1253/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib#diff-am9ibGliL19tZW1tYXBwaW5nX3JlZHVjZXIucHk=) | `61.36% <0.00%> (-35.25%)` | :arrow_down: |
| [joblib/executor.py](https://codecov.io/gh/joblib/joblib/pull/1253/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib#diff-am9ibGliL2V4ZWN1dG9yLnB5) | `76.19% <0.00%> (-23.81%)` | :arrow_down: |
| [joblib/format\_stack.py](https://codecov.io/gh/joblib/joblib/pull/1253/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib#diff-am9ibGliL2Zvcm1hdF9zdGFjay5weQ==) | `83.33% <0.00%> (-16.67%)` | :arrow_down: |
| [joblib/pool.py](https://codecov.io/gh/joblib/joblib/pull/1253/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib#diff-am9ibGliL3Bvb2wucHk=) | `72.13% <0.00%> (-15.68%)` | :arrow_down: |
| [joblib/my\_exceptions.py](https://codecov.io/gh/joblib/joblib/pull/1253/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib#diff-am9ibGliL215X2V4Y2VwdGlvbnMucHk=) | `85.71% <0.00%> (-14.29%)` | :arrow_down: |
| [joblib/test/test\_parallel.py](https://codecov.io/gh/joblib/joblib/pull/1253/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib#diff-am9ibGliL3Rlc3QvdGVzdF9wYXJhbGxlbC5weQ==) | `84.48% <0.00%> (-11.29%)` | :arrow_down: |
| [joblib/disk.py](https://codecov.io/gh/joblib/joblib/pull/1253/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib#diff-am9ibGliL2Rpc2sucHk=) | `80.95% <0.00%> (-11.12%)` | :arrow_down: |
| ... and [21 more](https://codecov.io/gh/joblib/joblib/pull/1253/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/joblib/joblib/pull/1253?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/joblib/joblib/pull/1253?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib). Last update [7742f58...3b39ce9](https://codecov.io/gh/joblib/joblib/pull/1253?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=joblib).


Patch:
diff --git a/README.rst b/README.rst
index a17427f..1746fb8 100644
--- a/README.rst
+++ b/README.rst
@@ -48,7 +48,7 @@ Dependencies
 ============
 
 - Joblib has no mandatory dependencies besides Python (supported versions are
-  3.6+).
+  3.7+).
 - Joblib has an optional dependency on Numpy (at least version 1.6.1) for array
   manipulation.
 - Joblib includes its own vendored copy of
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index ccc9f7f..61ce5a9 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -54,33 +54,33 @@ jobs:
         imageName: 'ubuntu-latest'
         PYTHON_VERSION: "3.7"
         EXTRA_CONDA_PACKAGES: "numpy=1.15 distributed=2.13"
-      linux_py36_cython:
+      linux_py37_cython:
         imageName: 'ubuntu-latest'
-        PYTHON_VERSION: "3.6"
-        EXTRA_CONDA_PACKAGES: "numpy=1.14"
+        PYTHON_VERSION: "3.7"
+        EXTRA_CONDA_PACKAGES: "numpy=1.15"
         CYTHON: "true"
-      linux_py36_no_multiprocessing_no_lzma:
+      linux_py37_no_multiprocessing_no_lzma:
         imageName: 'ubuntu-latest'
-        PYTHON_VERSION: "3.6"
-        EXTRA_CONDA_PACKAGES: "numpy=1.14"
+        PYTHON_VERSION: "3.7"
+        EXTRA_CONDA_PACKAGES: "numpy=1.15"
         JOBLIB_MULTIPROCESSING: "0"
         NO_LZMA: "1"
-      linux_py36_no_numpy:
+      linux_py37_no_numpy:
         imageName: 'ubuntu-latest'
-        PYTHON_VERSION: "3.6"
+        PYTHON_VERSION: "3.7"
 
       windows_py38:
-        imageName: "vs2017-win2016"
+        imageName: "windows-latest"
         PYTHON_VERSION: "3.8"
         EXTRA_CONDA_PACKAGES: "numpy=1.18"
 
       macos_py38:
-        imageName: "macos-10.14"
+        imageName: "macos-latest"
         PYTHON_VERSION: "3.8"
         EXTRA_CONDA_PACKAGES: "numpy=1.18"
-      macos_py36_no_numpy:
-        imageName: "macos-10.14"
-        PYTHON_VERSION: "3.6"
+      macos_py37_no_numpy:
+        imageName: "macos-latest"
+        PYTHON_VERSION: "3.7"
 
   variables:
     JUNITXML: 'test-data.xml'
diff --git a/continuous_integration/install.sh b/continuous_integration/install.sh
index d191e14..9f01f51 100755
--- a/continuous_integration/install.sh
+++ b/continuous_integration/install.sh
@@ -18,7 +18,7 @@ create_new_conda_env() {
 }
 
 create_new_pypy3_env() {
-    PYPY_FOLDER="pypy3.6-v7.3.1-linux64"
+    PYPY_FOLDER="pypy3.7-v7.3.7-linux64"
     wget https://downloads.python.org/pypy/$PYPY_FOLDER.tar.bz2
     tar xvf $PYPY_FOLDER.tar.bz2
     $PYPY_FOLDER/bin/pypy3 -m venv pypy3
diff --git a/examples/serialization_and_wrappers.py b/examples/serialization_and_wrappers.py
index 4e9967d..d03f012 100644
--- a/examples/serialization_and_wrappers.py
+++ b/examples/serialization_and_wrappers.py
@@ -36,7 +36,7 @@ print(Parallel(n_jobs=2)(delayed(func_async)(21) for _ in range(1))[0])
 
 
 ###############################################################################
-# For most use-cases, using ``cloudpickle``` is efficient enough. However, this
+# For most use-cases, using ``cloudpickle`` is efficient enough. However, this
 # solution can be very slow to serialize large python objects, such as dict or
 # list, compared to the standard ``pickle`` serialization.
 #
diff --git a/setup.py b/setup.py
index d2794c2..8686eaa 100755
--- a/setup.py
+++ b/setup.py
@@ -28,10 +28,10 @@ setup(
         'License :: OSI Approved :: BSD License',
         'Operating System :: OS Independent',
         'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: 3.7',
         'Programming Language :: Python :: 3.8',
         'Programming Language :: Python :: 3.9',
+        'Programming Language :: Python :: 3.10',
         'Topic :: Scientific/Engineering',
         'Topic :: Utilities',
         'Topic :: Software Development :: Libraries',
@@ -54,5 +54,5 @@ setup(
         'joblib.externals', 'joblib.externals.cloudpickle',
         'joblib.externals.loky', 'joblib.externals.loky.backend',
     ],
-    python_requires='>=3.6',
+    python_requires='>=3.7',
 )


Test patch:
diff --git a/continuous_integration/run_tests.sh b/continuous_integration/run_tests.sh
index 91ebd08..7c82ec3 100755
--- a/continuous_integration/run_tests.sh
+++ b/continuous_integration/run_tests.sh
@@ -28,14 +28,20 @@ if [[ "$SKLEARN_TESTS" == "true" ]]; then
     # Install scikit-learn from conda and test against the installed
     # development version of joblib.
     conda remove -y numpy
-    conda install -y -c conda-forge cython pillow scikit-learn
+    conda install -y -c conda-forge cython pillow pip numpy scipy
+    pip install --pre --extra-index https://pypi.anaconda.org/scipy-wheels-nightly/simple scikit-learn
     python -c "import sklearn; print('Testing scikit-learn', sklearn.__version__)"
 
     # Move to a dedicated folder to avoid being polluted by joblib specific conftest.py
     # and disable the doctest plugin to avoid issues with doctests in scikit-learn
     # docstrings that require setting print_changed_only=True temporarily.
     cd "/tmp"
-    pytest -vl --maxfail=5 -p no:doctest -k "not test_import_is_deprecated" --pyargs sklearn
+    pytest -vl --maxfail=5 -p no:doctest \
+        # Don't worry about deprecated imports: this is tested for real
+        # in upstream scikit-learn and this is not joblib's responsibility.
+        # Let's skip this test to avoid false positives in joblib's CI.
+        -k "not test_import_is_deprecated" \
+        --pyargs sklearn
 fi
 
 if [[ "$SKIP_TESTS" != "true" && "$COVERAGE" == "true" ]]; then
diff --git a/joblib/test/test_deprecated_objects.py b/joblib/test/test_deprecated_objects.py
index d561483..9ca6b08 100644
--- a/joblib/test/test_deprecated_objects.py
+++ b/joblib/test/test_deprecated_objects.py
@@ -2,15 +2,12 @@
 Tests making sure that deprecated objects properly raise a deprecation warning
 when imported/created.
 """
-import sys
-
 import pytest
 
 from joblib.my_exceptions import _deprecated_names as _deprecated_exceptions
 from joblib.format_stack import _deprecated_names as _deprecated_format_utils
 
 
-@pytest.mark.xfail(sys.version_info < (3, 7), reason="no module-level getattr")
 def test_deprecated_joblib_exceptions():
     assert 'JoblibException' in _deprecated_exceptions
     for name in _deprecated_exceptions:
@@ -20,7 +17,6 @@ def test_deprecated_joblib_exceptions():
             exec('from joblib.my_exceptions import {}'.format(name))
 
 
-@pytest.mark.xfail(sys.version_info < (3, 7), reason="no module-level getattr")
 def test_deprecated_formatting_utilities(capsys):
     assert 'safe_repr' in _deprecated_format_utils
     assert 'eq_repr' in _deprecated_format_utils
diff --git a/joblib/test/test_memmapping.py b/joblib/test/test_memmapping.py
index dc40d23..30a1b74 100644
--- a/joblib/test/test_memmapping.py
+++ b/joblib/test/test_memmapping.py
@@ -815,10 +815,13 @@ def test_child_raises_parent_exits_cleanly(backend):
                     temp_folder = get_temp_folder(p, "{b}")
                     p(delayed(print_filename_and_raise)(data)
                               for i in range(1))
-            except ValueError:
+            except ValueError as e:
                 # the temporary folder should be deleted by the end of this
                 # call
-                assert not os.path.exists(temp_folder)
+                if os.path.exists(temp_folder):
+                    raise AssertionError(
+                        temp_folder + " was not deleted"
+                    ) from e
     """.format(b=backend)
     env = os.environ.copy()
     env['PYTHONPATH'] = os.path.dirname(__file__)
@@ -828,7 +831,7 @@ def test_child_raises_parent_exits_cleanly(backend):
     out, err = p.communicate()
     out, err = out.decode(), err.decode()
     filename = out.split('\n')[0]
-    assert p.returncode == 0, out
+    assert p.returncode == 0, err or out
     assert err == ''  # no resource_tracker warnings.
     assert not os.path.exists(filename)