Repository: yt-project/yt

Issue:
pytest is now a hidden hard dependency
<!--To help us understand and resolve your issue, please fill out the form to
the best of your ability.-->
<!--You can feel free to delete the sections that do not apply.-->

### Bug report

We now import `pytest` directly in `yt/testing.py`, but `pytest` is not yet listed as a hard dependency. This will cause a `ModuleNotFoundError` to be raised after installing yt until you install pytest manually.

I'm opening an issue rather than a PR because I don't know whether people would prefer to put the `pytest` import in a try block or make it a dependency.


Discussion:
And is `yt/testing.py` imported anywhere in the codebase (outside the tests themselves)?
> And is yt/testing.py imported anywhere in the codebase (outside the tests themselves)?

I didn't think so... but here's  a minimal reproducer
```
cd /tmp/
python3.9 -m venv .env && source .env/bin/activate
pip install --upgrade pip
pip install git+https://github.com/yt-project/yt.git
python -c "import yt"
```

```
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/private/tmp/yt_test/.env/lib/python3.9/site-packages/yt/__init__.py", line 92, in <module>
    from yt.testing import run_nose
  File "/private/tmp/yt_test/.env/lib/python3.9/site-packages/yt/testing.py", line 15, in <module>
    import pytest
ModuleNotFoundError: No module named 'pytest'
```

I would argue that the _ideal_ solution would be to not import from yt.testing in `yt.__init__`
The only cost is that the `run_nose` function wouldn't be available from the `yt` namespace, which I hope is acceptable to everyone given that we have to get rid of nose eventually anyway.
Also: I checked that this only affects the dev branch, it hasn't been shipped in any release so far

Resolving patch:
diff --git a/yt/__init__.py b/yt/__init__.py
--- a/yt/__init__.py
+++ b/yt/__init__.py
@@ -89,7 +89,17 @@
     load_uniform_grid,
     load_unstructured_mesh,
 )
-from yt.testing import run_nose
+
+
+def run_nose(*args, **kwargs):
+    # we hide this function behind a closure so we
+    # don't make pytest a hard dependency for end users
+    # see https://github.com/yt-project/yt/issues/3771
+    from yt.testing import run_nose
+
+    return run_nose(*args, **kwargs)
+
+
 from yt.units.unit_systems import UnitSystem, unit_system_registry  # type: ignore
 
 # Import some helpful math utilities


Test patch:
diff --git a/yt/testing.py b/yt/testing.py
--- a/yt/testing.py
+++ b/yt/testing.py
@@ -17,6 +17,7 @@
 from numpy.random import RandomState
 from unyt.exceptions import UnitOperationError
 
+from yt._maintenance.deprecation import issue_deprecation_warning
 from yt.config import ytcfg
 from yt.funcs import is_sequence
 from yt.loaders import load
@@ -1190,6 +1191,12 @@ def run_nose(
     call_pdb=False,
     module=None,
 ):
+    issue_deprecation_warning(
+        "yt.run_nose (aka yt.testing.run_nose) is deprecated. "
+        "Please do not rely on this function as it will be removed "
+        "in the process of migrating yt tests from nose to pytest.",
+        since="4.1.0",
+    )
     import sys
 
     from yt.utilities.logger import ytLogger as mylog