Repository: napari/napari

Issue:
Valid magic kwargs check too strict?
## 🐛 Bug

I think the valid magic kwargs check from our plugins added in #2080 is too strict here https://github.com/napari/napari/blob/b3dd593ea1b29e48e38ef3729bced196ab6aab6d/napari/plugins/__init__.py#L136-L142

For example if I have 

```python
def gaussian_blur(
    layer: 'napari.layers.Image', sigma: float = 1.0, mode="nearest"
) -> 'napari.layers.Image':
    """Apply a gaussian blur to ``layer``."""
    if layer:
        return skimage.filters.gaussian(layer.data, sigma=sigma, mode=mode)


# Define our magic. The function will be automatically called when the
# input values are changed
magic_gaussian_blur = {
    'auto_call': True,
    'sigma': {"widget_type": "FloatSlider", "max": 6},
    'mode': {"choices": ["reflect", "constant", "nearest", "mirror", "wrap"]},
}
```

then I'm getting a warning

```python-traceback
/Users/nsofroniew/GitHub/napari/napari/plugins/__init__.py:138: UserWarning: Plugin 'io-test' provided invalid magicgui kwargs to `napari_experimental_provide_function_widget` for function 'gaussian_blur': {'mode', 'sigma'}. Widget ignored.
  f'Plugin {plugin_name!r} provided invalid magicgui kwargs '
```

Unless we decided those sorts of annotations shouldn't be possible, but then we should drop them from our example https://github.com/napari/napari/blob/b3dd593ea1b29e48e38ef3729bced196ab6aab6d/examples/magic_parameter_sweep.py#L29-L33

Thoughts @tlambert03? 


Discussion:
I agree that the check is too strict. This should definitely be allowed, imho.
Oh yeah, that’s just incorrect!  Sorry about that.  I’ll submit a fix.  

Resolving patch:
diff --git a/napari/plugins/__init__.py b/napari/plugins/__init__.py
--- a/napari/plugins/__init__.py
+++ b/napari/plugins/__init__.py
@@ -89,7 +89,11 @@ def register_dock_widget(
         dock_widgets[key] = (_cls, kwargs)
 
 
-valid_magic_kwargs = set(signature(magicgui).parameters)
+magicgui_sig = {
+    name
+    for name, p in signature(magicgui).parameters.items()
+    if p.kind is p.KEYWORD_ONLY
+}
 
 
 def register_function_widget(
@@ -133,11 +137,14 @@ def register_function_widget(
             )
             continue
 
-        if set(magic_kwargs) - valid_magic_kwargs:
+        valid_magic_kwargs = set(signature(func).parameters) | magicgui_sig
+        extra_kwargs = set(magic_kwargs) - valid_magic_kwargs
+        if extra_kwargs:
             warn(
                 f'Plugin {plugin_name!r} provided invalid magicgui kwargs '
                 f'to {hook_name} for function {func.__name__!r}: '
-                f'{set(magic_kwargs) - valid_magic_kwargs}. Widget ignored.'
+                f'{extra_kwargs}. Valid kwargs are {valid_magic_kwargs}.'
+                'Widget ignored.'
             )
             continue
 


Test patch:
diff --git a/napari/plugins/_tests/test_plugin_widgets.py b/napari/plugins/_tests/test_plugin_widgets.py
--- a/napari/plugins/_tests/test_plugin_widgets.py
+++ b/napari/plugins/_tests/test_plugin_widgets.py
@@ -25,6 +25,7 @@ def func2(x, y):
     'bad_tuple3': (func, 1, {}),
     'bad_double_tuple': ((func, {}), (func2, {})),
     'bad_magic_kwargs': (func, {"non_magicgui_kwarg": True}),
+    'good_magic_kwargs': (func, {'call_button': True, "x": {'max': 200}}),
 }