Repository: scverse/scanpy

Issue:
importlib_metadata >= 2.0 breaks scanpy.logging.print_versions()
- [x] I have checked that this issue has not already been reported.
- [x] I have confirmed this bug exists on the latest version of scanpy.
- [x] (optional) I have confirmed this bug exists on the master branch of scanpy.

---

When scanpy gets installed with the latest version of `importlib_metadata` (2.0), the 
command `sc.logging.print_versions()` fails with the following error: 

```pytb
WARNING: If you miss a compact list, please try `print_header`!
Traceback (most recent call last):
  File "/home/sturm/anaconda3/envs/scanpy_test/lib/python3.7/site-packages/sinfo/main.py", line 195, in sinfo
    mod_version = _find_version(mod.__version__)
AttributeError: module 'importlib_metadata' has no attribute '__version__'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/sturm/anaconda3/envs/scanpy_test/lib/python3.7/site-packages/scanpy/logging.py", line 161, in print_versions
    sinfo(dependencies=True)
  File "/home/sturm/anaconda3/envs/scanpy_test/lib/python3.7/site-packages/sinfo/main.py", line 198, in sinfo
    mod_version = _find_version(mod.version)
  File "/home/sturm/anaconda3/envs/scanpy_test/lib/python3.7/site-packages/sinfo/main.py", line 42, in _find_version
    return mod_version_attr()
TypeError: version() missing 1 required positional argument: 'distribution_name'
```

According to the `importlib_metadata` changelog, the `__version__` attribute has been removed from the package: 

```
=========================
 importlib_metadata NEWS
=========================

v2.0.0
======

* ``importlib_metadata`` no longer presents a
  ``__version__`` attribute. Consumers wishing to
  resolve the version of the package should query it
  directly with
  ``importlib_metadata.version('importlib-metadata')``.
  Closes #71.
```



Discussion:
While trying to fix it, I figured that the problem is upstream in `sinfo`. 
Here's the issue I created: 
https://gitlab.com/joelostblom/sinfo/-/issues/8

Feel free to close, or keep open until the problem is solved there. 

Resolving patch:
diff --git a/scanpy/logging.py b/scanpy/logging.py
--- a/scanpy/logging.py
+++ b/scanpy/logging.py
@@ -165,7 +165,17 @@ def print_versions(*, file=None):
     stdout = sys.stdout
     try:
         buf = sys.stdout = io.StringIO()
-        sinfo(dependencies=True)
+        sinfo(
+            dependencies=True,
+            excludes=[
+                'builtins',
+                'stdlib_list',
+                'importlib_metadata',
+                # Special module present if test coverage being calculated
+                # https://gitlab.com/joelostblom/sinfo/-/issues/10
+                "$coverage",
+            ],
+        )
     finally:
         sys.stdout = stdout
     output = buf.getvalue()


Test patch:
diff --git a/scanpy/tests/test_logging.py b/scanpy/tests/test_logging.py
--- a/scanpy/tests/test_logging.py
+++ b/scanpy/tests/test_logging.py
@@ -1,10 +1,12 @@
-import sys
+from contextlib import redirect_stdout
 from datetime import datetime
 from io import StringIO
+import sys
 
 import pytest
 
 from scanpy import Verbosity, settings as s, logging as l
+import scanpy as sc
 
 
 @pytest.fixture
@@ -91,3 +93,24 @@ def now(tz):
     assert counter == 4 and capsys.readouterr().err == '4 (0:00:02)\n'
     l.info('5 {time_passed}', time=start)
     assert counter == 5 and capsys.readouterr().err == '5 0:00:03\n'
+
+
+@pytest.mark.parametrize(
+    "func",
+    [
+        sc.logging.print_header,
+        sc.logging.print_versions,
+        sc.logging.print_version_and_date,
+    ],
+)
+def test_call_outputs(func):
+    """
+    Tests that these functions print to stdout and don't error.
+
+    Checks that https://github.com/theislab/scanpy/issues/1437 is fixed.
+    """
+    output_io = StringIO()
+    with redirect_stdout(output_io):
+        func()
+    output = output_io.getvalue()
+    assert output != ""