Repository: napari/napari

Issue:
Raise an informative exception when a non-existent plugin is requested
## 🐛 Bug

If you invoke napari from the command line with the `--plugin` flag with a plugin that does not exist, napari responds with a generic error message that doesn't disclose that the plugin doesn't exist. This can lead someone into thinking a plugin is malfunctioning, when it simply hasn't been installed correctly (e.g., because the `pip` command got hijacked by pyenv 😡 )

## To Reproduce

Steps to reproduce the behavior:

```bash
(napari) ➜  ~ napari --plugin not_a_plugin /path/to/data
Traceback (most recent call last):
  File "/Users/bennettd/mambaforge/envs/napari/bin/napari", line 10, in <module>
    sys.exit(main())
  File "/Users/bennettd/mambaforge/envs/napari/lib/python3.9/site-packages/napari/__main__.py", line 446, in main
    _run()
  File "/Users/bennettd/mambaforge/envs/napari/lib/python3.9/site-packages/napari/__main__.py", line 311, in _run
    viewer._window._qt_viewer._qt_open(
  File "/Users/bennettd/mambaforge/envs/napari/lib/python3.9/site-packages/napari/_qt/qt_viewer.py", line 754, in _qt_open
    self.viewer.open(
  File "/Users/bennettd/mambaforge/envs/napari/lib/python3.9/site-packages/napari/components/viewer_model.py", line 931, in open
    self._add_layers_with_plugins(
  File "/Users/bennettd/mambaforge/envs/napari/lib/python3.9/site-packages/napari/components/viewer_model.py", line 1134, in _add_layers_with_plugins
    layer_data, hookimpl = read_data_with_plugins(
  File "/Users/bennettd/mambaforge/envs/napari/lib/python3.9/site-packages/napari/plugins/io.py", line 68, in read_data_with_plugins
    res = _npe2.read(paths, plugin, stack=stack)
  File "/Users/bennettd/mambaforge/envs/napari/lib/python3.9/site-packages/napari/plugins/_npe2.py", line 54, in read
    raise e from e
  File "/Users/bennettd/mambaforge/envs/napari/lib/python3.9/site-packages/napari/plugins/_npe2.py", line 50, in read
    layer_data, reader = read_get_reader(npe1_path, plugin_name=plugin)
  File "/Users/bennettd/mambaforge/envs/napari/lib/python3.9/site-packages/npe2/io_utils.py", line 66, in read_get_reader
    return _read(
  File "/Users/bennettd/mambaforge/envs/napari/lib/python3.9/site-packages/npe2/io_utils.py", line 169, in _read
    raise ValueError(
ValueError: Plugin 'not_a_plugin' was selected to open ['/path/to/data'], but returned no data.
```


## Expected behavior

If I try to use a plugin that doesn't exist, napari should tell me that the plugin doesn't exist.

## Environment

```
napari: 0.4.16
Platform: macOS-12.5-arm64-arm-64bit
System: MacOS 12.5
Python: 3.9.16 | packaged by conda-forge | (main, Feb 1 2023, 21:38:11) [Clang 14.0.6 ]
Qt: 5.15.6
PyQt5: 5.15.7
NumPy: 1.24.3
SciPy: 1.9.1
Dask: 2023.6.0
VisPy: 0.10.0

OpenGL:
- GL version: 2.1 Metal - 76.3
- MAX_TEXTURE_SIZE: 16384

Screens:
- screen 1: resolution 1470x956, scale 2.0

Plugins:
- console: 0.0.8
- napari-ome-zarr: 0.5.2
- napari-svg: 0.1.7
- scikit-image: 0.4.16
```



Discussion:
Thanks for the report.
I notice you're on 0.4.16. On 0.4.17 (current version) I get:
```
ValueError: There is no registered plugin named 'not_a_plugin'.
Names of plugins offering readers are: {'ome-types', 'example-plugin'}
```
if I provide a valid path (your example with an invalid path gives `ValueError: Requested path '/path/to/data' does not exist.`)
Could you try updating napari or making a new env with updated napari?
Is that information clear enough?

Edit: note this is with npe2 0.6.1
I updated to 0.4.17 and I see the same message as before.

<details><summary>Napari info</summary>

```
napari: 0.4.17
Platform: macOS-12.5-arm64-arm-64bit
System: MacOS 12.5
Python: 3.9.16 | packaged by conda-forge | (main, Feb 1 2023, 21:38:11) [Clang 14.0.6 ]
Qt: 5.15.6
PyQt5: 5.15.7
NumPy: 1.25.0
SciPy: 1.9.1
Dask: 2023.6.0
VisPy: 0.11.0
magicgui: 0.7.2
superqt: unknown
in-n-out: 0.1.7
app-model: 0.1.4
npe2: 0.7.0

OpenGL:
- GL version: 2.1 Metal - 76.3
- MAX_TEXTURE_SIZE: 16384

Screens:
- screen 1: resolution 1470x956, scale 2.0

```

</details> 
Ok so we seem to have a regression with npe2 0.7.0 & 0.4.17 (shown in your env) vs. my npe2 0.6.1 & 0.4.17.
Updating my 0.4.17 env to 0.7.0 gives the less informative error you have.

However, on 0.4.18rc1 I do get the more informative message again:
```
ValueError: There is no registered plugin named 'not_a_plugin'.
Names of plugins offering readers are: set()
```

`Names of plugins offering readers are: set()`

This is also a bit wrong, no? Users might not know that `set()` represents an empty python data structure rather than a strangely named plugin. I think it would be better to have a special case for the situation where there are no plugins e.g., no plugins offering readers -> "There are no plugins offering readers", some plugins offering readers -> "The following plugins offer readers: [names of plugins]"


It's a fresh env with no plugins installed, so an edge case perhaps? But yeah, being more explicit (`no plugins are installed`) would be clearer.
> Ok so we seem to have a regression with npe2 0.7.0 & 0.4.17

A few people seem to be running into this so it might be worth adding some kind of check similar to what @Czaki suggested [here](https://github.com/napari/napari/issues/5837#issuecomment-1549418047)? 

Also agree, should simplify the error message when there's no readers installed. 
@DragaDoncila Could you check if this error is still present on 0.4.18? As this release may be the best place to solve it. 
> Could you check if this error is still present on 0.4.18? As this release may be the best place to solve it.

The release candidate shows 

```python
ValueError: There is no registered plugin named 'not-a-plugin'.
Names of plugins offering readers are: set()
```

I can open a PR tomorrow to make the error message clearer if you want to include it for 0.4.18
Yes. Please. 

Resolving patch:
diff --git a/napari/plugins/io.py b/napari/plugins/io.py
--- a/napari/plugins/io.py
+++ b/napari/plugins/io.py
@@ -100,13 +100,27 @@ def read_data_with_plugins(
             raise ValueError(message)
 
         if plugin not in plugin_manager.plugins:
-            names = {i.plugin_name for i in hook_caller.get_hookimpls()}
+            names = set(_npe2.get_readers().keys()).union(
+                {i.plugin_name for i in hook_caller.get_hookimpls()}
+            )
+            err_helper = (
+                trans._(
+                    "No readers are available. "
+                    "Do you have any plugins installed?",
+                    deferred=True,
+                )
+                if len(names) <= 1
+                else trans._(
+                    f"\nNames of plugins offering readers are: {names}.",
+                    deferred=True,
+                )
+            )
             raise ValueError(
                 trans._(
-                    "There is no registered plugin named '{plugin}'.\nNames of plugins offering readers are: {names}",
+                    "There is no registered plugin named '{plugin}'. {err_helper}",
                     deferred=True,
                     plugin=plugin,
-                    names=names,
+                    err_helper=err_helper,
                 )
             )
         reader = hook_caller._call_plugin(plugin, path=npe1_path)


Test patch: