Repository: nipy/nipype

Issue:
Test on Python 3.8
### Summary

Python 3.8 has been out for a month or so, and upstream libraries are mostly providing wheels. We should start 3.8 tests when it's feasible.

Necessary steps:

* [ ] Update `.travis.yml` to use Python 3.8
* [ ] Note any failures, identify whether they exist on our end or in a dependency
* [ ] Find blocking upstream issues and link to them so we can track
* [ ] When tests pass and urgent `FutureWarning`/`DeprecationWarning`s are dealt with, update the classifiers in `nipype/info.py` to indicate we support Python 3.8.

I marked this [![good-first-issue](https://img.shields.io/github/labels/nipy/nipype/good-first-issue)](https://github.com/nipy/nipype/labels/good-first-issue) but it's less for a new developer than someone looking to get more involved in project maintenance.


Discussion:

Resolving patch:
diff --git a/nipype/info.py b/nipype/info.py
--- a/nipype/info.py
+++ b/nipype/info.py
@@ -56,6 +56,7 @@ def get_nipype_gitversion():
     "Operating System :: POSIX :: Linux",
     "Programming Language :: Python :: 3.6",
     "Programming Language :: Python :: 3.7",
+    "Programming Language :: Python :: 3.8",
     "Topic :: Scientific/Engineering",
 ]
 PYTHON_REQUIRES = ">= 3.6"
@@ -109,6 +110,7 @@ def get_nipype_gitversion():
 FUTURE_MIN_VERSION = "0.16.0"
 SIMPLEJSON_MIN_VERSION = "3.8.0"
 PROV_VERSION = "1.5.2"
+RDFLIB_MIN_VERSION = "5.0.0"
 CLICK_MIN_VERSION = "6.6.0"
 PYDOT_MIN_VERSION = "1.2.3"
 
@@ -143,6 +145,7 @@ def get_nipype_gitversion():
     "pydot>=%s" % PYDOT_MIN_VERSION,
     "pydotplus",
     "python-dateutil>=%s" % DATEUTIL_MIN_VERSION,
+    "rdflib>=%s" % RDFLIB_MIN_VERSION,
     "scipy>=%s" % SCIPY_MIN_VERSION,
     "simplejson>=%s" % SIMPLEJSON_MIN_VERSION,
     "traits>=%s,!=5.0" % TRAITS_MIN_VERSION,
@@ -150,10 +153,6 @@ def get_nipype_gitversion():
     "etelemetry>=0.2.0",
 ]
 
-# neurdflib has to come after prov
-# https://github.com/nipy/nipype/pull/2961#issuecomment-512035484
-REQUIRES += ["neurdflib"]
-
 TESTS_REQUIRES = [
     "codecov",
     "coverage<5",


Test patch:
diff --git a/nipype/pipeline/plugins/tests/test_legacymultiproc_nondaemon.py b/nipype/pipeline/plugins/tests/test_legacymultiproc_nondaemon.py
--- a/nipype/pipeline/plugins/tests/test_legacymultiproc_nondaemon.py
+++ b/nipype/pipeline/plugins/tests/test_legacymultiproc_nondaemon.py
@@ -134,6 +134,7 @@ def run_multiproc_nondaemon_with_flag(nondaemon_flag):
     return result
 
 
+@pytest.mark.skipif(sys.version_info >= (3, 8), reason="multiprocessing issues in Python 3.8")
 def test_run_multiproc_nondaemon_false():
     """
     This is the entry point for the test. Two times a pipe of several
@@ -152,6 +153,7 @@ def test_run_multiproc_nondaemon_false():
     assert shouldHaveFailed
 
 
+@pytest.mark.skipif(sys.version_info >= (3, 8), reason="multiprocessing issues in Python 3.8")
 def test_run_multiproc_nondaemon_true():
     # with nondaemon_flag = True, the execution should succeed
     result = run_multiproc_nondaemon_with_flag(True)
diff --git a/nipype/pipeline/plugins/tests/test_multiproc.py b/nipype/pipeline/plugins/tests/test_multiproc.py
--- a/nipype/pipeline/plugins/tests/test_multiproc.py
+++ b/nipype/pipeline/plugins/tests/test_multiproc.py
@@ -4,6 +4,7 @@
 """
 Test the resource management of MultiProc
 """
+import sys
 import os
 import pytest
 from nipype.pipeline import engine as pe
@@ -33,6 +34,7 @@ def _list_outputs(self):
         return outputs
 
 
+@pytest.mark.skipif(sys.version_info >= (3, 8), reason="multiprocessing issues in Python 3.8")
 def test_run_multiproc(tmpdir):
     tmpdir.chdir()
 
@@ -114,6 +116,7 @@ def test_no_more_threads_than_specified(tmpdir):
         pipe.run(plugin="MultiProc", plugin_args={"n_procs": max_threads})
 
 
+@pytest.mark.skipif(sys.version_info >= (3, 8), reason="multiprocessing issues in Python 3.8")
 def test_hold_job_until_procs_available(tmpdir):
     tmpdir.chdir()
 
diff --git a/nipype/pipeline/plugins/tests/test_oar.py b/nipype/pipeline/plugins/tests/test_oar.py
--- a/nipype/pipeline/plugins/tests/test_oar.py
+++ b/nipype/pipeline/plugins/tests/test_oar.py
@@ -29,8 +29,9 @@ def _list_outputs(self):
         return outputs
 
 
-@pytest.mark.skipif(which('oarsub') is None, reason="OAR not installed")
-def test_run_pbsgraph(tmp_path):
+@pytest.mark.skipif(which("oarsub") is None, reason="OAR not installed")
+@pytest.mark.timeout(60)
+def test_run_oargraph(tmp_path):
     pipe = pe.Workflow(name="pipe", base_dir=str(tmp_path))
     mod1 = pe.Node(interface=OarTestInterface(), name="mod1")
     mod2 = pe.MapNode(interface=OarTestInterface(), iterfield=["input1"], name="mod2")
diff --git a/nipype/pipeline/plugins/tests/test_pbs.py b/nipype/pipeline/plugins/tests/test_pbs.py
--- a/nipype/pipeline/plugins/tests/test_pbs.py
+++ b/nipype/pipeline/plugins/tests/test_pbs.py
@@ -29,7 +29,8 @@ def _list_outputs(self):
         return outputs
 
 
-@pytest.mark.skipif(which('qsub') is None, reason="PBS not installed")
+@pytest.mark.skipif(which("qsub") is None, reason="PBS not installed")
+@pytest.mark.timeout(60)
 def test_run_pbsgraph(tmp_path):
     pipe = pe.Workflow(name="pipe", base_dir=str(tmp_path))
     mod1 = pe.Node(interface=PbsTestInterface(), name="mod1")
diff --git a/nipype/utils/tests/test_provenance.py b/nipype/utils/tests/test_provenance.py
--- a/nipype/utils/tests/test_provenance.py
+++ b/nipype/utils/tests/test_provenance.py
@@ -15,6 +15,7 @@
 
 
 @needs_rdflib5
+@pytest.mark.timeout(60)
 def test_provenance(tmpdir):
     from nipype.interfaces.base import CommandLine
 
@@ -27,6 +28,7 @@ def test_provenance(tmpdir):
 
 
 @needs_rdflib5
+@pytest.mark.timeout(60)
 def test_provenance_exists(tmpdir):
     tmpdir.chdir()
     from nipype import config