Repository: napari/napari

Issue:
napari --plugin-info does not list contributions from npe2 plugins
## 🐛 Bug

As noted by @tlambert03 in #4445, `napari --plugin-info` only lists hookimpl information about old-style napari_plugin_engine plugins, not npe2 plugins.


Discussion:
Currently the output for me looks like this:
```
PluginManager for "napari"
(12 hook specs and 1 plugins)
---------------------------------------------
console v0.0.4  
```

Should we now include both the hook specs and npe2 plugin information?
Yes, but there’s a lot in flux here at the moment,  so it’s not immediately clear how best to do this in a way that won’t change very soon.   See https://github.com/napari/npe2/pull/192 which is doing exactly this for npe2, so the feature is there… but the “merging” with npe1 plugin manager output is the issue 
Thanks, will leave for now then!

Resolving patch:
diff --git a/napari/__main__.py b/napari/__main__.py
--- a/napari/__main__.py
+++ b/napari/__main__.py
@@ -16,18 +16,14 @@
 class InfoAction(argparse.Action):
     def __call__(self, *args, **kwargs):
         # prevent unrelated INFO logs when doing "napari --info"
+        from npe2 import cli
+
         from napari.utils import sys_info
 
         logging.basicConfig(level=logging.WARNING)
         print(sys_info())
-        from .plugins import plugin_manager
-
-        plugin_manager.discover_widgets()
-        if errors := plugin_manager.get_errors():
-            names = {e.plugin_name for e in errors}
-            print("\n!!  Errors were detected in the following plugins:")
-            print("(Run 'napari --plugin-info -v' for more details)")
-            print("\n".join(f"  - {n}" for n in names))
+        print("Plugins:")
+        cli.list(fields="", sort="0", format="compact")
         sys.exit()
 
 
@@ -35,29 +31,13 @@ class PluginInfoAction(argparse.Action):
     def __call__(self, *args, **kwargs):
         # prevent unrelated INFO logs when doing "napari --info"
         logging.basicConfig(level=logging.WARNING)
-        from .plugins import plugin_manager
-
-        plugin_manager.discover_widgets()
-        print(plugin_manager)
-
-        if errors := plugin_manager.get_errors():
-            print("!!  Some errors occurred:")
-            verbose = '-v' in sys.argv or '--verbose' in sys.argv
-            if not verbose:
-                print("   (use '-v') to show full tracebacks")
-            print("-" * 38)
-
-            for err in errors:
-                print(err.plugin_name)
-                print(f"  error: {err!r}")
-                print(f"  cause: {err.__cause__!r}")
-                if verbose:
-                    print("  traceback:")
-                    import traceback
-                    from textwrap import indent
-
-                    tb = traceback.format_tb(err.__cause__.__traceback__)
-                    print(indent("".join(tb), '   '))
+        from npe2 import cli
+
+        cli.list(
+            fields="name,version,npe2,contributions",
+            sort="name",
+            format="table",
+        )
         sys.exit()
 
 
diff --git a/napari/conftest.py b/napari/conftest.py
--- a/napari/conftest.py
+++ b/napari/conftest.py
@@ -486,6 +486,7 @@ def builtins(_npe2pm: TestPluginManager):
 @pytest.fixture
 def tmp_plugin(_npe2pm: TestPluginManager):
     with _npe2pm.tmp_plugin() as plugin:
+        plugin.manifest.package_metadata = {'version': '0.1.0', 'name': 'test'}
         yield plugin
 
 
diff --git a/napari/utils/info.py b/napari/utils/info.py
--- a/napari/utils/info.py
+++ b/napari/utils/info.py
@@ -76,10 +76,6 @@ def sys_info(as_html=False):
     as_html : bool
         if True, info will be returned as HTML, suitable for a QTextEdit widget
     """
-    from npe2 import PluginManager as Npe2PluginManager
-
-    from napari.plugins import plugin_manager
-
     sys_version = sys.version.replace('\n', ' ')
     text = (
         f"<b>napari</b>: {napari.__version__}<br>"
@@ -153,32 +149,6 @@ def sys_info(as_html=False):
     except Exception as e:
         text += f"  - failed to load screen information {e}"
 
-    plugin_manager.discover()
-    plugin_strings = {}
-    for meta in plugin_manager.list_plugin_metadata():
-        plugin_name = meta.get('plugin_name')
-        if plugin_name == 'builtins':
-            continue
-        version = meta.get('version')
-        version_string = f": {version}" if version else ""
-        plugin_strings[plugin_name] = f"  - {plugin_name}{version_string}"
-
-    npe2_plugin_manager = Npe2PluginManager.instance()
-    for manifest in npe2_plugin_manager.iter_manifests():
-        plugin_name = manifest.name
-        if plugin_name in ("napari", "builtins"):
-            continue
-        version = manifest.package_version
-        version_string = f": {version}" if version else ""
-        plugin_strings[plugin_name] = f"  - {plugin_name}{version_string}"
-
-    text += '<br><b>Plugins</b>:'
-    text += (
-        ("<br>" + "<br>".join(sorted(plugin_strings.values())))
-        if plugin_strings
-        else '  None'
-    )
-
     if not as_html:
         text = (
             text.replace("<br>", "\n").replace("<b>", "").replace("</b>", "")


Test patch:
diff --git a/napari/_tests/test_sys_info.py b/napari/_tests/test_sys_info.py
--- a/napari/_tests/test_sys_info.py
+++ b/napari/_tests/test_sys_info.py
@@ -8,7 +8,6 @@ def test_sys_info(qapp):
     assert isinstance(str_info, str)
     assert '<br>' not in str_info
     assert '<b>' not in str_info
-    assert "Plugins" in str_info
 
     html_info = sys_info(as_html=True)
     assert isinstance(html_info, str)