Repository: pytest-dev/pytest-xdist (python)

Issue:
Handy API to know information about master/slaves from session
As mentioned in https://github.com/smarie/python-pytest-harvest/issues/36 (and discussed already with @nicoddemus in https://github.com/smarie/python-pytest-harvest/issues/32) it would be great to have an elegant API to answer these simple questions:

 - `get_xdist_worker_id(session)`: would return `session.config.slaveinput['slaveid']` or raise an exception if this not an xdist worker
 - `is_xdist_worker(session)`: would return `True` if this is an xdist worker, `False` otherwise
 - `is_xdist_master(session)`: would return `True` if this is the xdist master, `False` otherwise (**important**: this would return `False` when distribution has not been activated and "normal" pytest has been run on a single process)

Would you like a PR for this ?

When/if this API is done, we should not forget to post the answer on [this SO question](https://stackoverflow.com/questions/60212389/how-can-i-know-from-inside-a-pytest-hook-if-pytest-xdist-is-enabled)

Pull request:
New methods `is_xdist_worker`, `is_xdist_master`, `get_xdist_worker_id`. 
Fixes #504 .

I did not create any additional test as I am not sure how/where these should be put.


Patch:
diff --git a/.appveyor.yml b/.appveyor.yml
index 36c98c1..0c9cef7 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -1,6 +1,5 @@
 environment:
   matrix:
-  - TOXENV: "py27-pytestlatest"
   - TOXENV: "py35-pytestlatest"
   - TOXENV: "py36-pytestlatest"
   - TOXENV: "py37-pytestlatest"
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 04d0ce3..c735868 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -3,7 +3,7 @@ repos:
     rev: 19.10b0
     hooks:
     -   id: black
-        args: [--safe, --quiet]
+        args: [--safe, --quiet, --target-version, py35]
         language_version: python3.7
 -   repo: https://github.com/pre-commit/pre-commit-hooks
     rev: v2.4.0
diff --git a/.travis.yml b/.travis.yml
index 8932051..be759da 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -32,8 +32,6 @@ jobs:
           - $HOME/.cache/pre-commit
     - python: '3.8'
       env: TOXENV=py38-pytestlatest
-    - python: '2.7'
-      env: TOXENV=py27-pytestlatest
 
     - stage: test
       python: "3.5"
@@ -42,6 +40,8 @@ jobs:
       env: TOXENV=py36-pytestlatest
     - python: "3.7"
       env: TOXENV=py37-pytestlatest
+    - python: "3.9-dev"
+      env: TOXENV=py39-pytestlatest
     - python: "3.8"
       env: TOXENV=py38-pytestmaster
 
diff --git a/ISSUES.txt b/ISSUES.txt
deleted file mode 100644
index 58cf69d..0000000
--- a/ISSUES.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-next release critical
------------------------------------------------
-tag: bug
-
-miserably fails: --dist=each --tx popen --tx socket=...
-
-
-rename / hooks
------------------------------------------------
-tag: bug
-
-node -> slave
-transition for hooks?
-configure_node -> configure_slave
-
-allow to remotely run xdist tests with xdist
------------------------------------------------
-tag: feature
-
-allow to run xdist own tests using its own mechanism.
-currently this doesn't work because the remote side
-has no pytest plugin.   How to configure/do
-register "xdist.plugin" on the remote side?
-
-see to avoid any "from _pytest" internal imports
------------------------------------------------
-tag: feature
-
-currently tests and even xdist core code imports
-names from the internal _pytest namespace.
-See to avoid it.
diff --git a/README.rst b/README.rst
index ca13819..c62dd75 100644
--- a/README.rst
+++ b/README.rst
@@ -268,7 +268,7 @@ a test or fixture, you may use the ``worker_id`` fixture to do so:
 When ``xdist`` is disabled (running with ``-n0`` for example), then
 ``worker_id`` will return ``"master"``.
 
-Additionally, worker processes have the following environment variables
+Worker processes also have the following environment variables
 defined:
 
 * ``PYTEST_XDIST_WORKER``: the name of the worker, e.g., ``"gw2"``.
@@ -278,6 +278,34 @@ defined:
 The information about the worker_id in a test is stored in the ``TestReport`` as
 well, under the ``worker_id`` attribute.
 
+Since version 2.0, the following functions are also available in the ``xdist`` module:
+
+.. code-block:: python
+
+    def is_xdist_worker(request_or_session) -> bool:
+        """Return `True` if this is an xdist worker, `False` otherwise
+
+        :param request_or_session: the `pytest` `request` or `session` object
+        """
+
+    def is_xdist_master(request_or_session) -> bool:
+        """Return `True` if this is the xdist master, `False` otherwise
+
+        Note: this method also returns `False` when distribution has not been
+        activated at all.
+
+        :param request_or_session: the `pytest` `request` or `session` object
+        """
+
+    def get_xdist_worker_id(request_or_session) -> str:
+        """Return the id of the current worker ('gw0', 'gw1', etc) or 'master'
+        if running on the 'master' node.
+
+        If not distributing tests (for example passing `-n0` or not passing `-n` at all) also return 'master'.
+
+        :param request_or_session: the `pytest` `request` or `session` object
+        """
+
 
 Uniquely identifying the current test run
 -----------------------------------------
diff --git a/changelog/504.feature.rst b/changelog/504.feature.rst
new file mode 100644
index 0000000..2348898
--- /dev/null
+++ b/changelog/504.feature.rst
@@ -0,0 +1,1 @@
+New functions ``xdist.is_xdist_worker``, ``xdist.is_xdist_master``, ``xdist.get_xdist_worker_id``, to easily identify the current node.
diff --git a/changelog/541.removal.rst b/changelog/541.removal.rst
new file mode 100644
index 0000000..f8f3f44
--- /dev/null
+++ b/changelog/541.removal.rst
@@ -0,0 +1,1 @@
+Drop backward-compatibility "slave" aliases related to worker nodes.  We deliberately moved away from this terminology years ago, and it seems like the right time to finish the deprecation and removal process.
diff --git a/changelog/541.trivial.rst b/changelog/541.trivial.rst
new file mode 100644
index 0000000..5911940
--- /dev/null
+++ b/changelog/541.trivial.rst
@@ -0,0 +1,1 @@
+``pytest-xdist`` now requires ``pytest>=6.0``.
diff --git a/changelog/569.removal.rst b/changelog/569.removal.rst
new file mode 100644
index 0000000..9207182
--- /dev/null
+++ b/changelog/569.removal.rst
@@ -0,0 +1,1 @@
+``pytest-xdist`` no longer supports Python 2.7.
diff --git a/setup.py b/setup.py
index ad4675c..fcba298 100644
--- a/setup.py
+++ b/setup.py
@@ -1,12 +1,6 @@
 from setuptools import setup, find_packages
 
-install_requires = [
-    "execnet>=1.1",
-    "psutil>=3.0.0",
-    "pytest>=4.4.0",
-    "pytest-forked",
-    "six",
-]
+install_requires = ["execnet>=1.1", "psutil>=3.0.0", "pytest>=6.0.0", "pytest-forked"]
 
 
 with open("README.rst") as f:
@@ -15,8 +9,7 @@ with open("README.rst") as f:
 setup(
     name="pytest-xdist",
     use_scm_version={"write_to": "src/xdist/_version.py"},
-    description="pytest xdist plugin for distributed testing"
-    " and loop-on-failing modes",
+    description="pytest xdist plugin for distributed testing and loop-on-failing modes",
     long_description=long_description,
     license="MIT",
     author="holger krekel and contributors",
@@ -30,7 +23,7 @@ setup(
         "pytest11": ["xdist = xdist.plugin", "xdist.looponfail = xdist.looponfail"]
     },
     zip_safe=False,
-    python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
+    python_requires=">=3.5",
     install_requires=install_requires,
     setup_requires=["setuptools_scm"],
     classifiers=[
@@ -45,9 +38,8 @@ setup(
         "Topic :: Software Development :: Quality Assurance",
         "Topic :: Utilities",
         "Programming Language :: Python",
-        "Programming Language :: Python :: 2",
-        "Programming Language :: Python :: 2.7",
         "Programming Language :: Python :: 3",
+        "Programming Language :: Python :: 3 :: Only",
         "Programming Language :: Python :: 3.5",
         "Programming Language :: Python :: 3.6",
         "Programming Language :: Python :: 3.7",
diff --git a/src/xdist/__init__.py b/src/xdist/__init__.py
index 44d995d..83ef776 100644
--- a/src/xdist/__init__.py
+++ b/src/xdist/__init__.py
@@ -1,3 +1,4 @@
+from xdist.plugin import is_xdist_worker, is_xdist_master, get_xdist_worker_id
 from xdist._version import version as __version__
 
-__all__ = ["__version__"]
+__all__ = ["__version__", "is_xdist_worker", "is_xdist_master", "get_xdist_worker_id"]
diff --git a/src/xdist/dsession.py b/src/xdist/dsession.py
index 2f06cb1..07ef091 100644
--- a/src/xdist/dsession.py
+++ b/src/xdist/dsession.py
@@ -10,14 +10,14 @@ from xdist.scheduler import (
 )
 
 
-from six.moves.queue import Empty, Queue
+from queue import Empty, Queue
 
 
 class Interrupted(KeyboardInterrupt):
     """ signals an immediate interruption. """
 
 
-class DSession(object):
+class DSession:
     """A pytest plugin which runs a distributed test session
 
     At the beginning of the test session this creates a NodeManager
@@ -150,9 +150,6 @@ class DSession(object):
         node.workerinfo["id"] = node.gateway.id
         node.workerinfo["spec"] = node.gateway.spec
 
-        # TODO: (#234 task) needs this for pytest. Remove when refactor in pytest repo
-        node.slaveinfo = node.workerinfo
-
         self.config.hook.pytest_testnodeready(node=node)
         if self.shuttingdown:
             node.shutdown()
@@ -271,11 +268,6 @@ class DSession(object):
         assert not rep.passed
         self._failed_worker_collectreport(node, rep)
 
-    def worker_logwarning(self, message, code, nodeid, fslocation):
-        """Emitted when a node calls the pytest_logwarning hook."""
-        kwargs = dict(message=message, code=code, nodeid=nodeid, fslocation=fslocation)
-        self.config.hook.pytest_logwarning.call_historic(kwargs=kwargs)
-
     def worker_warning_captured(self, warning_message, when, item):
         """Emitted when a node calls the pytest_warning_captured hook (deprecated in 6.0)."""
         kwargs = dict(warning_message=warning_message, when=when, item=item)
@@ -336,7 +328,7 @@ class DSession(object):
         self.config.hook.pytest_runtest_logreport(report=rep)
 
 
-class TerminalDistReporter(object):
+class TerminalDistReporter:
     def __init__(self, config):
         self.config = config
         self.tr = config.pluginmanager.getplugin("terminalreporter")
diff --git a/src/xdist/looponfail.py b/src/xdist/looponfail.py
index 7f01ad6..19b9313 100644
--- a/src/xdist/looponfail.py
+++ b/src/xdist/looponfail.py
@@ -6,7 +6,6 @@
     processes) otherwise changes to source code can crash
     the controlling process which should best never happen.
 """
-from __future__ import print_function
 import py
 import pytest
 import sys
@@ -55,7 +54,7 @@ def looponfail_main(config):
         print()
 
 
-class RemoteControl(object):
+class RemoteControl:
     def __init__(self, config):
         self.config = config
         self.failures = []
@@ -165,7 +164,7 @@ def init_worker_session(channel, args, option_dict):
     WorkerFailSession(config, channel).main()
 
 
-class WorkerFailSession(object):
+class WorkerFailSession:
     def __init__(self, config, channel):
         self.config = config
         self.channel = channel
@@ -220,7 +219,7 @@ class WorkerFailSession(object):
         self.channel.send((trails, failreports, self.collection_failed))
 
 
-class StatRecorder(object):
+class StatRecorder:
     def __init__(self, rootdirlist):
         self.rootdirlist = rootdirlist
         self.statcache = {}
diff --git a/src/xdist/plugin.py b/src/xdist/plugin.py
index 162f4c9..9244de2 100644
--- a/src/xdist/plugin.py
+++ b/src/xdist/plugin.py
@@ -43,14 +43,11 @@ def pytest_addoption(parser):
     )
     group.addoption(
         "--max-worker-restart",
-        "--max-slave-restart",
         action="store",
         default=None,
         dest="maxworkerrestart",
         help="maximum number of workers that can be restarted "
-        "when crashed (set to zero to disable this feature)\n"
-        "'--max-slave-restart' option is deprecated and will be removed in "
-        "a future release",
+        "when crashed (set to zero to disable this feature)",
     )
     group.addoption(
         "--dist",
@@ -195,19 +192,53 @@ def pytest_cmdline_main(config):
 
 
 # -------------------------------------------------------------------------
-# fixtures
+# fixtures and API to easily know the role of current node
 # -------------------------------------------------------------------------
 
 
+def is_xdist_worker(request_or_session) -> bool:
+    """Return `True` if this is an xdist worker, `False` otherwise
+
+    :param request_or_session: the `pytest` `request` or `session` object
+    """
+    return hasattr(request_or_session.config, "workerinput")
+
+
+def is_xdist_master(request_or_session) -> bool:
+    """Return `True` if this is the xdist master, `False` otherwise
+
+    Note: this method also returns `False` when distribution has not been
+    activated at all.
+
+    :param request_or_session: the `pytest` `request` or `session` object
+    """
+    return (
+        not is_xdist_worker(request_or_session)
+        and request_or_session.config.option.dist != "no"
+    )
+
+
+def get_xdist_worker_id(request_or_session) -> str:
+    """Return the id of the current worker ('gw0', 'gw1', etc) or 'master'
+    if running on the 'master' node.
+
+    If not distributing tests (for example passing `-n0` or not passing `-n` at all)
+    also return 'master'.
+
+    :param request_or_session: the `pytest` `request` or `session` object
+    """
+    if hasattr(request_or_session.config, "workerinput"):
+        return request_or_session.config.workerinput["workerid"]
+    else:
+        return "master"
+
+
 @pytest.fixture(scope="session")
 def worker_id(request):
     """Return the id of the current worker ('gw0', 'gw1', etc) or 'master'
     if running on the master node.
     """
-    if hasattr(request.config, "workerinput"):
-        return request.config.workerinput["workerid"]
-    else:
-        return "master"
+    return get_xdist_worker_id(request)
 
 
 @pytest.fixture(scope="session")
diff --git a/src/xdist/remote.py b/src/xdist/remote.py
index 86ba9cb..3d1b246 100644
--- a/src/xdist/remote.py
+++ b/src/xdist/remote.py
@@ -18,7 +18,7 @@ from execnet.gateway_base import dumps, DumpError
 from _pytest.config import _prepareconfig, Config
 
 
-class WorkerInteractor(object):
+class WorkerInteractor:
     def __init__(self, config, channel):
         self.config = config
         self.workerid = config.workerinput.get("workerid", "?")
@@ -125,20 +125,6 @@ class WorkerInteractor(object):
             )
             self.sendevent("collectreport", data=data)
 
-    # the pytest_logwarning hook was deprecated since pytest 4.0
-    if hasattr(
-        _pytest.hookspec, "pytest_logwarning"
-    ) and not _pytest.hookspec.pytest_logwarning.pytest_spec.get("warn_on_impl"):
-
-        def pytest_logwarning(self, message, code, nodeid, fslocation):
-            self.sendevent(
-                "logwarning",
-                message=message,
-                code=code,
-                nodeid=nodeid,
-                fslocation=str(fslocation),
-            )
-
     # the pytest_warning_recorded hook was introduced in pytest 6.0
     if hasattr(_pytest.hookspec, "pytest_warning_recorded"):
 
@@ -266,8 +252,5 @@ if __name__ == "__channelexec__":
     config._parser.prog = os.path.basename(workerinput["mainargv"][0])
     config.workerinput = workerinput
     config.workeroutput = {}
-    # TODO: deprecated name, backward compatibility only. Remove it in future
-    config.slaveinput = config.workerinput
-    config.slaveoutput = config.workeroutput
     interactor = WorkerInteractor(config, channel)
     config.hook.pytest_cmdline_main(config=config)
diff --git a/src/xdist/report.py b/src/xdist/report.py
index 3d239f1..8843b40 100644
--- a/src/xdist/report.py
+++ b/src/xdist/report.py
@@ -1,4 +1,3 @@
-from __future__ import print_function
 from difflib import unified_diff
 
 
@@ -13,9 +12,9 @@ def report_collection_diff(from_collection, to_collection, from_id, to_id):
 
     diff = unified_diff(from_collection, to_collection, fromfile=from_id, tofile=to_id)
     error_message = (
-        u"Different tests were collected between {from_id} and {to_id}. "
-        u"The difference is:\n"
-        u"{diff}"
+        "Different tests were collected between {from_id} and {to_id}. "
+        "The difference is:\n"
+        "{diff}"
     ).format(from_id=from_id, to_id=to_id, diff="\n".join(diff))
     msg = "\n".join([x.rstrip() for x in error_message.split("\n")])
     return msg
diff --git a/src/xdist/scheduler/each.py b/src/xdist/scheduler/each.py
index 486d8b0..b2a0442 100644
--- a/src/xdist/scheduler/each.py
+++ b/src/xdist/scheduler/each.py
@@ -4,7 +4,7 @@ from xdist.workermanage import parse_spec_config
 from xdist.report import report_collection_diff
 
 
-class EachScheduling(object):
+class EachScheduling:
     """Implement scheduling of test items on all nodes
 
     If a node gets added after the test run is started then it is
diff --git a/src/xdist/scheduler/load.py b/src/xdist/scheduler/load.py
index 4116984..e378d9a 100644
--- a/src/xdist/scheduler/load.py
+++ b/src/xdist/scheduler/load.py
@@ -7,7 +7,7 @@ from xdist.workermanage import parse_spec_config
 from xdist.report import report_collection_diff
 
 
-class LoadScheduling(object):
+class LoadScheduling:
     """Implement load scheduling across nodes.
 
     This distributes the tests collected across all nodes so each test
diff --git a/src/xdist/scheduler/loadfile.py b/src/xdist/scheduler/loadfile.py
index 52a28b1..867a94e 100644
--- a/src/xdist/scheduler/loadfile.py
+++ b/src/xdist/scheduler/loadfile.py
@@ -21,7 +21,7 @@ class LoadFileScheduling(LoadScopeScheduling):
     """
 
     def __init__(self, config, log=None):
-        super(LoadFileScheduling, self).__init__(config, log)
+        super().__init__(config, log)
         if log is None:
             self.log = Producer("loadfilesched")
         else:
diff --git a/src/xdist/scheduler/loadscope.py b/src/xdist/scheduler/loadscope.py
index 6b50a48..31dbe26 100644
--- a/src/xdist/scheduler/loadscope.py
+++ b/src/xdist/scheduler/loadscope.py
@@ -6,7 +6,7 @@ from xdist.report import report_collection_diff
 from xdist.workermanage import parse_spec_config
 
 
-class LoadScopeScheduling(object):
+class LoadScopeScheduling:
     """Implement load scheduling across nodes, but grouping test by scope.
 
     This distributes the tests collected across all nodes so each test is run
diff --git a/src/xdist/workermanage.py b/src/xdist/workermanage.py
index e8b49bb..ec7f3df 100644
--- a/src/xdist/workermanage.py
+++ b/src/xdist/workermanage.py
@@ -1,4 +1,3 @@
-from __future__ import print_function
 import fnmatch
 import os
 import re
@@ -29,7 +28,7 @@ def parse_spec_config(config):
     return xspeclist
 
 
-class NodeManager(object):
+class NodeManager:
     EXIT_TIMEOUT = 10
     DEFAULT_IGNORES = [".*", "*.pyc", "*.pyo", "*~"]
 
@@ -167,7 +166,7 @@ class HostRSync(execnet.RSync):
         self._ignores = [
             re.compile(fnmatch.translate(getattr(x, "strpath", x))) for x in ignores
         ]
-        super(HostRSync, self).__init__(sourcedir=sourcedir, **kwargs)
+        super().__init__(sourcedir=sourcedir, **kwargs)
 
     def filter(self, path):
         path = py.path.local(path)
@@ -179,9 +178,7 @@ class HostRSync(execnet.RSync):
 
     def add_target_host(self, gateway, finished=None):
         remotepath = os.path.basename(self._sourcedir)
-        super(HostRSync, self).add_target(
-            gateway, remotepath, finishedcallback=finished, delete=True
-        )
+        super().add_target(gateway, remotepath, finishedcallback=finished, delete=True)
 
     def _report_send_file(self, gateway, modified_rel_path):
         if self._verbose:
@@ -211,7 +208,7 @@ def make_reltoroot(roots, args):
     return result
 
 
-class WorkerController(object):
+class WorkerController:
     ENDMARK = -1
 
     class RemoteHook:
@@ -228,13 +225,9 @@ class WorkerController(object):
         self.workerinput = {
             "workerid": gateway.id,
             "workercount": len(nodemanager.specs),
-            "slaveid": gateway.id,
-            "slavecount": len(nodemanager.specs),
             "testrunuid": nodemanager.testrunuid,
             "mainargv": sys.argv,
         }
-        # TODO: deprecated name, backward compatibility only. Remove it in future
-        self.slaveinput = self.workerinput
         self._down = False
         self._shutdown_sent = False
         self.log = py.log.Producer("workerctl-%s" % gateway.id)
@@ -296,7 +289,7 @@ class WorkerController(object):
         if not self._down:
             try:
                 self.sendcommand("shutdown")
-            except (IOError, OSError):
+            except OSError:
                 pass
             self._shutdown_sent = True
 
@@ -333,7 +326,7 @@ class WorkerController(object):
                 self.notify_inproc(eventname, node=self, **kwargs)
             elif eventname == "workerfinished":
                 self._down = True
-                self.workeroutput = self.slaveoutput = kwargs["workeroutput"]
+                self.workeroutput = kwargs["workeroutput"]
                 self.notify_inproc("workerfinished", node=self)
             elif eventname in ("logstart", "logfinish"):
                 self.notify_inproc(eventname, node=self, **kwargs)
diff --git a/tox.ini b/tox.ini
index a68913a..d532a2f 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,7 +1,7 @@
 [tox]
 envlist=
   linting
-  py{27,35,36,37,38}-pytestlatest
+  py{35,36,37,38,39}-pytestlatest
   py38-pytestmaster
 
 [testenv]


Test patch:
diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py
index 7f9a551..2812f5b 100644
--- a/testing/acceptance_test.py
+++ b/testing/acceptance_test.py
@@ -1,10 +1,9 @@
 import os
 import re
-import sys
-import textwrap
 
 import py
 import pytest
+import xdist
 
 
 class TestDistribution:
@@ -115,9 +114,6 @@ class TestDistribution:
         assert result.ret == 2
         result.stdout.fnmatch_lines(["*Interrupted: stopping*1*", "*1 failed*"])
 
-    @pytest.mark.xfail(
-        reason="#527: Python 3.8 failure in pytest where testdir.tmpdir returns an unexpected value"
-    )
     def test_basetemp_in_subprocesses(self, testdir):
         p1 = testdir.makepyfile(
             """
@@ -245,22 +241,6 @@ class TestDistribution:
         result.stderr.fnmatch_lines(["--foobar=123 active! *"])
         assert dest.join(subdir.basename).check(dir=1)
 
-    def test_backward_compatibility_worker_terminology(self, testdir):
-        """Ensure that we still support "config.slaveinput" for backward compatibility (#234).
-
-        Keep in mind that removing this compatibility will break a ton of plugins and user code.
-        """
-        testdir.makepyfile(
-            """
-            def test(pytestconfig):
-                assert hasattr(pytestconfig, 'slaveinput')
-                assert hasattr(pytestconfig, 'workerinput')
-        """
-        )
-        result = testdir.runpytest("-n1")
-        result.stdout.fnmatch_lines("*1 passed*")
-        assert result.ret == 0
-
     def test_data_exchange(self, testdir):
         testdir.makeconftest(
             """
@@ -593,16 +573,8 @@ def test_fixture_teardown_failure(testdir):
     assert result.ret
 
 
-@pytest.mark.skipif(
-    sys.version_info[:2] == (2, 7),
-    reason="Only available in pytest 5.0+ (Python 3 only)",
-)
 def test_config_initialization(testdir, monkeypatch, pytestconfig):
     """Ensure workers and master are initialized consistently. Integration test for #445"""
-    if not hasattr(pytestconfig, "invocation_params"):
-        pytest.skip(
-            "requires pytest >=5.1 (config has no attribute 'invocation_params')"
-        )
     testdir.makepyfile(
         **{
             "dir_a/test_foo.py": """
@@ -699,32 +671,6 @@ def test_skipping(testdir):
     result.stdout.fnmatch_lines(["*hello*", "*1 skipped*"])
 
 
-def test_issue34_pluginloading_in_subprocess(testdir):
-    import _pytest.hookspec
-
-    if not hasattr(_pytest.hookspec, "pytest_namespace"):
-        pytest.skip("this pytest version no longer supports pytest_namespace()")
-
-    testdir.tmpdir.join("plugin123.py").write(
-        textwrap.dedent(
-            """
-        def pytest_namespace():
-            return {'sample_variable': 'testing'}
-    """
-        )
-    )
-    testdir.makepyfile(
-        """
-        import pytest
-        def test_hello():
-            assert pytest.sample_variable == "testing"
-    """
-    )
-    result = testdir.runpytest_subprocess("-n1", "-p", "plugin123")
-    assert result.ret == 0
-    result.stdout.fnmatch_lines(["*1 passed*"])
-
-
 def test_fixture_scope_caching_issue503(testdir):
     p1 = testdir.makepyfile(
         """
@@ -802,27 +748,15 @@ def test_sub_plugins_disabled(testdir, plugin):
 
 class TestWarnings:
     @pytest.mark.parametrize("n", ["-n0", "-n1"])
-    @pytest.mark.parametrize("warn_type", ["pytest", "builtin"])
-    def test_warnings(self, testdir, n, request, warn_type):
-        if warn_type == "builtin":
-            warn_code = """warnings.warn(UserWarning('this is a warning'))"""
-        elif warn_type == "pytest":
-            if not hasattr(request.config, "warn"):
-                pytest.skip("config.warn has been removed in pytest 4.1")
-            warn_code = """request.config.warn('', 'this is a warning',
-                           fslocation=py.path.local())"""
-        else:
-            assert False
+    def test_warnings(self, testdir, n):
         testdir.makepyfile(
             """
             import warnings, py, pytest
 
             @pytest.mark.filterwarnings('ignore:config.warn has been deprecated')
             def test_func(request):
-                {warn_code}
-        """.format(
-                warn_code=warn_code
-            )
+                warnings.warn(UserWarning('this is a warning'))
+            """
         )
         result = testdir.runpytest(n)
         result.stdout.fnmatch_lines(["*this is a warning*", "*1 passed, 1 warning*"])
@@ -899,9 +833,6 @@ class TestWarnings:
         """Check that warnings with unserializable _WARNING_DETAILS are
         handled correctly (#379).
         """
-        if sys.version_info[0] < 3:
-            # The issue is only present in Python 3 warnings
-            return
         testdir.makepyfile(
             """
             import warnings, pytest
@@ -1436,3 +1367,39 @@ def get_workers_and_test_count_by_prefix(prefix, lines, expected_status="PASSED"
         if expected_status == status and nodeid.startswith(prefix):
             result[worker] = result.get(worker, 0) + 1
     return result
+
+
+class TestAPI:
+    @pytest.fixture
+    def fake_request(self):
+        class FakeOption:
+            def __init__(self):
+                self.dist = "load"
+
+        class FakeConfig:
+            def __init__(self):
+                self.workerinput = {"workerid": "gw5"}
+                self.option = FakeOption()
+
+        class FakeRequest:
+            def __init__(self):
+                self.config = FakeConfig()
+
+        return FakeRequest()
+
+    def test_is_xdist_worker(self, fake_request):
+        assert xdist.is_xdist_worker(fake_request)
+        del fake_request.config.workerinput
+        assert not xdist.is_xdist_worker(fake_request)
+
+    def test_is_xdist_master(self, fake_request):
+        assert not xdist.is_xdist_master(fake_request)
+        del fake_request.config.workerinput
+        assert xdist.is_xdist_master(fake_request)
+        fake_request.config.option.dist = "no"
+        assert not xdist.is_xdist_master(fake_request)
+
+    def test_get_xdist_worker_id(self, fake_request):
+        assert xdist.get_xdist_worker_id(fake_request) == "gw5"
+        del fake_request.config.workerinput
+        assert xdist.get_xdist_worker_id(fake_request) == "master"
diff --git a/testing/conftest.py b/testing/conftest.py
index 43c0399..52f0308 100644
--- a/testing/conftest.py
+++ b/testing/conftest.py
@@ -1,20 +1,9 @@
-import six
 import py
 import pytest
 import execnet
 
 pytest_plugins = "pytester"
 
-if six.PY2:
-
-    @pytest.fixture(scope="session", autouse=True)
-    def _ensure_imports():
-        # we import some modules because pytest-2.8's testdir fixture
-        # will unload all modules after each test and this cause
-        # (unknown) problems with execnet.Group()
-        execnet.Group
-        execnet.makegateway
-
 
 @pytest.fixture(autouse=True)
 def _divert_atexit(request, monkeypatch):
diff --git a/testing/test_dsession.py b/testing/test_dsession.py
index 8d0373e..b015c75 100644
--- a/testing/test_dsession.py
+++ b/testing/test_dsession.py
@@ -203,7 +203,7 @@ class TestLoadScheduling:
         different test ids are collected by workers.
         """
 
-        class CollectHook(object):
+        class CollectHook:
             """
             Dummy hook that stores collection reports.
             """
diff --git a/testing/test_remote.py b/testing/test_remote.py
index e223a66..da2f6a8 100644
--- a/testing/test_remote.py
+++ b/testing/test_remote.py
@@ -8,7 +8,7 @@ from xdist.workermanage import WorkerController
 import execnet
 import marshal
 
-from six.moves.queue import Queue
+from queue import Queue
 
 WAIT_TIMEOUT = 10.0
 
diff --git a/testing/test_slavemanage.py b/testing/test_slavemanage.py
index 47d2cba..f8abcca 100644
--- a/testing/test_slavemanage.py
+++ b/testing/test_slavemanage.py
@@ -1,4 +1,3 @@
-from __future__ import print_function
 import py
 import pytest
 import textwrap
@@ -35,7 +34,7 @@ def mysetup(tmpdir):
 
 @pytest.fixture
 def workercontroller(monkeypatch):
-    class MockController(object):
+    class MockController:
         def __init__(self, *args):
             pass