Repository: holoviz/holoviews

Issue:
%opts break after error
Reproduce with
```python
import holoviews as hv
hv.notebook_extension()
%opts Image [cmap='viridis']
```

then try:
```python
%opts Image (cmap='viridis')
```
which will print:
```
Unexpected plot option 'cmap' for Image in loaded backend 'matplotlib'.

Possible keywords in the currently active 'matplotlib' backend are: ['apply_extents', 'apply_ranges', 'apply_ticks', 'aspect', 'bgcolor', 'cbar_padding', 'cbar_ticks', 'cbar_width', 'clipping_colors', 'colorbar', 'fig_alpha', 'fig_bounds', 'fig_inches', 'fig_latex', 'fig_rcparams', 'fig_size', 'final_hooks', 'finalize_hooks', 'fontsize', 'initial_hooks', 'invert_axes', 'invert_xaxis', 'invert_yaxis', 'invert_zaxis', 'labelled', 'logx', 'logy', 'logz', 'normalize', 'projection', 'show_frame', 'show_grid', 'show_legend', 'show_title', 'situate_axes', 'sublabel_format', 'sublabel_position', 'sublabel_size', 'symmetric', 'title_format', 'xaxis', 'xrotation', 'xticks', 'yaxis', 'yrotation', 'yticks', 'zaxis', 'zrotation', 'zticks']

If you believe this keyword is correct, please make sure the backend has been imported or loaded with the notebook_extension.
In [ ]:

```


Discussion:
Thanks for reporting this.

It now seems to think the style option is a plot option for some reason. I'll need to investigate...

Resolving patch:
diff --git a/holoviews/core/options.py b/holoviews/core/options.py
--- a/holoviews/core/options.py
+++ b/holoviews/core/options.py
@@ -364,7 +364,7 @@ def __init__(self, key=None, allowed_keywords=[], merge_keywords=True, **kwargs)
             self.warning("Invalid options %s, valid options are: %s"
                          % (repr(invalid_kws), str(allowed_keywords)))
 
-        self.kwargs = kwargs
+        self.kwargs = {k:v for k,v in kwargs.items() if k not in invalid_kws}
         self._options = self._expand_options(kwargs)
         allowed_keywords = (allowed_keywords if isinstance(allowed_keywords, Keywords)
                             else Keywords(allowed_keywords))


Test patch:
diff --git a/tests/testoptions.py b/tests/testoptions.py
--- a/tests/testoptions.py
+++ b/tests/testoptions.py
@@ -2,7 +2,7 @@
 import pickle
 import numpy as np
 from holoviews import Store, StoreOptions, Histogram, Image
-from holoviews.core.options import OptionError, Cycle, Options, OptionTree
+from holoviews.core.options import OptionError, Cycle, Options, OptionTree, options_policy
 from holoviews.element.comparison import ComparisonTestCase
 from holoviews import plotting              # noqa Register backends
 from unittest import SkipTest
@@ -28,16 +28,20 @@ def test_options_init(self):
         Options('test')
 
     def test_options_valid_keywords1(self):
-        Options('test', allowed_keywords=['kw1'], kw1='value')
+        opts = Options('test', allowed_keywords=['kw1'], kw1='value')
+        self.assertEquals(opts.kwargs, {'kw1':'value'})
 
     def test_options_valid_keywords2(self):
-        Options('test', allowed_keywords=['kw1', 'kw2'], kw1='value')
+        opts = Options('test', allowed_keywords=['kw1', 'kw2'], kw1='value')
+        self.assertEquals(opts.kwargs, {'kw1':'value'})
 
     def test_options_valid_keywords3(self):
-        Options('test', allowed_keywords=['kw1', 'kw2'], kw1='value', kw2='value')
+        opts = Options('test', allowed_keywords=['kw1', 'kw2'], kw1='value1', kw2='value2')
+        self.assertEquals(opts.kwargs, {'kw1':'value1', 'kw2':'value2'})
 
     def test_options_any_keywords3(self):
-        Options('test', kw1='value', kw2='value')
+        opts = Options('test', kw1='value1', kw2='value3')
+        self.assertEquals(opts.kwargs, {'kw1':'value1', 'kw2':'value3'})
 
     def test_options_invalid_keywords1(self):
         try:
@@ -51,6 +55,26 @@ def test_options_invalid_keywords2(self):
         except OptionError as e:
             self.assertEqual(str(e), "Invalid option 'kw3', valid options are: ['kw2']")
 
+    def test_options_invalid_keywords_skip1(self):
+        with options_policy(skip_invalid=True, warn_on_skip=False):
+            opts = Options('test', allowed_keywords=['kw1'], kw='value')
+        self.assertEqual(opts.kwargs, {})
+
+    def test_options_invalid_keywords_skip2(self):
+        with options_policy(skip_invalid=True, warn_on_skip=False):
+            opts = Options('test', allowed_keywords=['kw1'], kw1='value', kw2='val')
+        self.assertEqual(opts.kwargs, {'kw1':'value'})
+
+
+    def test_options_record_invalid(self):
+        StoreOptions.start_recording_skipped()
+        with options_policy(skip_invalid=True, warn_on_skip=False):
+            opts = Options('test', allowed_keywords=['kw1'], kw1='value', kw2='val')
+        errors = StoreOptions.stop_recording_skipped()
+        self.assertEqual(len(errors),1)
+        self.assertEqual(errors[0].invalid_keyword,'kw2')
+
+
     def test_options_get_options(self):
         opts = Options('test', allowed_keywords=['kw2', 'kw3'],
                        kw2='value', kw3='value').options