Repository: pytest-dev/pytest-timeout (language: python)

Issue:
distutils is deprecated in Python 3.10 and later.
The `distutils` package is deprecated as of Python 3.10 and is slated for removal in Python 3.12

`pytest-timeout` uses `LooseVersion` here:

https://github.com/pytest-dev/pytest-timeout/blob/1a35f94298c1260500907bd674b75cf59df0c949/pytest_timeout.py#L397-L402

I wonder if this might be better served with a `hasattr` check for `read_global_capture`? Another option is to use the version numbers from `packaging`, which `pytest` already depends on and is the recommended replacement for handling version numbers.

-----

See [PEP 631](https://www.python.org/dev/peps/pep-0632/) for more information on the `distutils` deprecation.

Full traceback when running with `-Werror`:

<details>

```python traceback
Traceback (most recent call last):
  File "/usr/lib/python3.10/runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/usr/lib/python3.10/site-packages/pytest/__main__.py", line 5, in <module>
    raise SystemExit(pytest.console_main())
  File "/usr/lib/python3.10/site-packages/_pytest/config/__init__.py", line 185, in console_main
    code = main()
  File "/usr/lib/python3.10/site-packages/_pytest/config/__init__.py", line 143, in main
    config = _prepareconfig(args, plugins)
  File "/usr/lib/python3.10/site-packages/_pytest/config/__init__.py", line 318, in _prepareconfig
    config = pluginmanager.hook.pytest_cmdline_parse(
  File "/usr/lib/python3.10/site-packages/pluggy/hooks.py", line 286, in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
  File "/usr/lib/python3.10/site-packages/pluggy/manager.py", line 93, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/usr/lib/python3.10/site-packages/pluggy/manager.py", line 84, in <lambda>
    self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
  File "/usr/lib/python3.10/site-packages/pluggy/callers.py", line 203, in _multicall
    gen.send(outcome)
  File "/usr/lib/python3.10/site-packages/_pytest/helpconfig.py", line 100, in pytest_cmdline_parse
    config: Config = outcome.get_result()
  File "/usr/lib/python3.10/site-packages/pluggy/callers.py", line 80, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/usr/lib/python3.10/site-packages/pluggy/callers.py", line 187, in _multicall
    res = hook_impl.function(*args)
  File "/usr/lib/python3.10/site-packages/_pytest/config/__init__.py", line 1003, in pytest_cmdline_parse
    self.parse(args)
  File "/usr/lib/python3.10/site-packages/_pytest/config/__init__.py", line 1283, in parse
    self._preparse(args, addopts=addopts)
  File "/usr/lib/python3.10/site-packages/_pytest/config/__init__.py", line 1172, in _preparse
    self.pluginmanager.load_setuptools_entrypoints("pytest11")
  File "/usr/lib/python3.10/site-packages/pluggy/manager.py", line 299, in load_setuptools_entrypoints
    plugin = ep.load()
  File "/usr/lib/python3.10/importlib/metadata/__init__.py", line 160, in load
    module = import_module(match.group('module'))
  File "/usr/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
  File "/usr/lib/python3.10/site-packages/_pytest/assertion/rewrite.py", line 170, in exec_module
    exec(co, module.__dict__)
  File "/usr/lib/python3.10/site-packages/pytest_timeout.py", line 16, in <module>
    from distutils.version import LooseVersion
  File "/usr/lib/python3.10/distutils/__init__.py", line 19, in <module>
    warnings.warn(_DEPRECATION_MESSAGE,
DeprecationWarning: The distutils package is deprecated and slated for removal in Python 3.12. Use setuptools or check PEP 632 for potential alternatives

```

</details>


Discussion / hints:
Yeah, I noticed this recently as well.  Seems a bit silly to do this version sniffing.  I looked through some history and there seems no good reason given.  It just started out simple and then was made more and more complicated.  I actually wonder if today we can not just entirely remove this code and simply require on a pytest that's more recent in setup.py?

Would you like to make a PR for that?

Fix patch:
diff --git a/README.rst b/README.rst
index 03e09cd..75c24ca 100644
--- a/README.rst
+++ b/README.rst
@@ -218,6 +218,11 @@ session using ```--pdb``` or similar.
 Changelog
 =========
 
+2.1.0
+-----
+
+- Increase pytest requirement to >=5.0.0.  Thanks Dominic Davis-Foster.
+
 2.0.0
 -----
 
diff --git a/setup.py b/setup.py
index 31b5b90..cdbca3a 100644
--- a/setup.py
+++ b/setup.py
@@ -16,7 +16,7 @@
     license="MIT",
     py_modules=["pytest_timeout"],
     entry_points={"pytest11": ["timeout = pytest_timeout"]},
-    install_requires=["pytest>=3.6.0"],
+    install_requires=["pytest>=5.0.0"],
     classifiers=[
         "Development Status :: 5 - Production/Stable",
         "Environment :: Console",


Test patch:
diff --git a/pytest_timeout.py b/pytest_timeout.py
index b2b05e6..d420b82 100644
--- a/pytest_timeout.py
+++ b/pytest_timeout.py
@@ -13,7 +13,6 @@
 import threading
 import traceback
 from collections import namedtuple
-from distutils.version import LooseVersion
 
 import py
 import pytest
@@ -394,12 +393,8 @@ def timeout_timer(item, timeout):
     try:
         capman = item.config.pluginmanager.getplugin("capturemanager")
         if capman:
-            pytest_version = LooseVersion(pytest.__version__)
-            if pytest_version >= LooseVersion("3.7.3"):
-                capman.suspend_global_capture(item)
-                stdout, stderr = capman.read_global_capture()
-            else:
-                stdout, stderr = capman.suspend_global_capture(item)
+            capman.suspend_global_capture(item)
+            stdout, stderr = capman.read_global_capture()
         else:
             stdout, stderr = None, None
         write_title("Timeout", sep="+")