Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replacement for cell magic with tab-completion #3173

Merged
merged 35 commits into from
Nov 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
b857670
Prototype of new and improved options mechanism
jlstevens Nov 12, 2018
ba16635
Removed validation from opts completers
jlstevens Nov 12, 2018
f11faeb
Skipping processing of dotted OptionTree entries
jlstevens Nov 12, 2018
35d943b
Updated .options to process *args
jlstevens Nov 12, 2018
5a26dec
Now supporting a mixture of Option objects and dictionaries
jlstevens Nov 12, 2018
ab170aa
Fixed hole in Options repr
jlstevens Nov 12, 2018
441108b
Fixed bug in options *args processing
jlstevens Nov 12, 2018
e11a964
Now correctly merging options
jlstevens Nov 12, 2018
c842376
Fixed .options signature for Python 2
jlstevens Nov 13, 2018
721ae22
Fixed processing of *args in Dimensioned .options method
jlstevens Nov 13, 2018
f354876
Updated .options signature on HoloMap and DynamicMap
jlstevens Nov 13, 2018
22e4067
Updated existing options unit tests
jlstevens Nov 13, 2018
4a909ee
Improved message when using undefined lowercase key in Options
jlstevens Nov 13, 2018
ab27041
Renamed _expected_groups to _option_groups
jlstevens Nov 13, 2018
a828fb4
Fixed bug in .options and deleted trailing whitespace
jlstevens Nov 13, 2018
95c5a51
Fixed bug in hv.opts validation
jlstevens Nov 13, 2018
d562031
Fixed incorrect search and replace
jlstevens Nov 13, 2018
99663c2
Moved merge_option_dicts utility out of util.parser
jlstevens Nov 13, 2018
9d1b4b3
Set config.warn_options_call to True
jlstevens Nov 13, 2018
347ee9a
Updated warning issued when deprecated __call__ is used
jlstevens Nov 13, 2018
34f2318
Using set_current_backend in plotting extensions
jlstevens Nov 13, 2018
f328def
Fixed warning string
jlstevens Nov 13, 2018
6a11776
Made the _update_backends hook robust to unavailable backends
jlstevens Nov 13, 2018
5900103
Added tab completion to hv.output
jlstevens Nov 14, 2018
5b3503c
Preserving original docstring for hv.opts
jlstevens Nov 14, 2018
4e2c4fc
Improved missing backend exception message in opts.expand_options
jlstevens Nov 15, 2018
df76294
Skipping cross backend tests when matplotlib unavailable
jlstevens Nov 15, 2018
d3fa13b
Added another SkipTest when missing matplotlib
jlstevens Nov 15, 2018
79950f6
Added unit test of completer update switching backends
jlstevens Nov 15, 2018
941eb15
Merge branch 'master' into options_extension
jlstevens Nov 16, 2018
a56cd51
Removed use of __call__ instead of .opts in testoptions.py
jlstevens Nov 16, 2018
ebc619f
Added four unit tests of the .options method
jlstevens Nov 16, 2018
2959ab8
Using set_current_backend in new unit tests
jlstevens Nov 16, 2018
ae1d562
Made inner completer into a classmethod
jlstevens Nov 16, 2018
9fb9a1a
Setting cmap explicitly in unit test
jlstevens Nov 16, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions holoviews/core/dimension.py
Original file line number Diff line number Diff line change
Expand Up @@ -1156,7 +1156,9 @@ def __unicode__(self):
def __call__(self, options=None, **kwargs):
if config.warn_options_call:
self.warning('Use of __call__ to set options will be deprecated '
'in future. Use the equivalent opts method instead.')
'in future. Use the equivalent opts method or use '
'the recommended .options method instead.')

return self.opts(options, **kwargs)

def opts(self, options=None, backend=None, clone=True, **kwargs):
Expand Down Expand Up @@ -1225,7 +1227,7 @@ def opts(self, options=None, backend=None, clone=True, **kwargs):
return obj


def options(self, options=None, backend=None, clone=True, **kwargs):
def options(self, *args, **kwargs):
"""
Applies options on an object or nested group of objects in a
flat format returning a new object with the options
Expand All @@ -1246,15 +1248,26 @@ def options(self, options=None, backend=None, clone=True, **kwargs):
If no options are supplied all options on the object will be reset.
Disabling clone will modify the object inplace.
"""
if isinstance(options, basestring):
options = {options: kwargs}
elif options and kwargs:
backend = kwargs.pop('backend', None)
clone = kwargs.pop('clone', True)

if len(args) == 0 and len(kwargs)==0:
options = None
elif args and isinstance(args[0], basestring):
options = {args[0]: kwargs}
elif args and isinstance(args[0], list):
if kwargs:
raise ValueError('Please specify a list of option objects, or kwargs, but not both')
options = args[0]
elif args and kwargs:
raise ValueError("Options must be defined in one of two formats."
"Either supply keywords defining the options for "
"the current object, e.g. obj.options(cmap='viridis'), "
"or explicitly define the type, e.g."
"obj.options({'Image': {'cmap': 'viridis'}})."
"Supplying both formats is not supported.")
elif args:
options = list(args)
elif kwargs:
options = {type(self).__name__: kwargs}

Expand Down
28 changes: 26 additions & 2 deletions holoviews/core/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,8 @@ class Options(param.Parameterized):
skipping over invalid keywords or not. May only be specified at
the class level.""")

_option_groups = ['style', 'plot', 'norm']

def __init__(self, key=None, allowed_keywords=[], merge_keywords=True, max_cycles=None, **kwargs):

invalid_kws = []
Expand All @@ -402,6 +404,10 @@ def __init__(self, key=None, allowed_keywords=[], merge_keywords=True, max_cycle
else:
raise OptionError(kwarg, allowed_keywords)

if key and key[0].islower() and key not in self._option_groups:
raise Exception('Key %s does not start with a capitalized element class name and is not a group in %s'
% (repr(key), ', '.join(repr(el) for el in self._option_groups)))

for invalid_kw in invalid_kws:
error = OptionError(invalid_kw, allowed_keywords, group_name=key)
StoreOptions.record_skipped_option(error)
Expand Down Expand Up @@ -495,8 +501,14 @@ def options(self):


def __repr__(self):
kws = ', '.join("%s=%r" % (k,v) for (k,v) in self.kwargs.items())
return "%s(%s)" % (self.__class__.__name__, kws)
kws = ', '.join("%s=%r" % (k,self.kwargs[k]) for k in sorted(self.kwargs.keys()))

if self.key and self.key[0].isupper() and kws:
return "%s(%s, %s)" % (self.__class__.__name__, repr(self.key), kws)
elif self.key and self.key[0].isupper():
return "%s(%s)" % (self.__class__.__name__, repr(self.key))
else:
return "%s(%s)" % (self.__class__.__name__, kws)

def __str__(self):
return repr(self)
Expand Down Expand Up @@ -617,6 +629,9 @@ def __setattr__(self, identifier, val):
group_items = val
elif isinstance(val, Options) and val.key is None:
raise AttributeError("Options object needs to have a group name specified.")
elif isinstance(val, Options) and val.key[0].isupper():
raise AttributeError("OptionTree only accepts Options using keys that are one of %s." %
', '.join(repr(el) for el in Options._option_groups))
elif isinstance(val, Options):
group_items = {val.key: val}
elif isinstance(val, OptionTree):
Expand Down Expand Up @@ -1052,6 +1067,15 @@ class Store(object):

current_backend = 'matplotlib'

_backend_switch_hooks = []

@classmethod
def set_current_backend(cls, backend):
"Use this method to set the backend to run the switch hooks"
for hook in cls._backend_switch_hooks:
hook(backend)
cls.current_backend = backend

@classmethod
def options(cls, backend=None, val=None):
backend = cls.current_backend if backend is None else backend
Expand Down
13 changes: 7 additions & 6 deletions holoviews/core/spaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def opts(self, options=None, backend=None, clone=True, **kwargs):
return self.clone(data)


def options(self, options=None, backend=None, clone=True, **kwargs):
def options(self, *args, **kwargs):
"""
Applies options on an object or nested group of objects in a
flat format returning a new object with the options
Expand All @@ -118,7 +118,7 @@ def options(self, options=None, backend=None, clone=True, **kwargs):
If no options are supplied all options on the object will be reset.
Disabling clone will modify the object inplace.
"""
data = OrderedDict([(k, v.options(options, backend, clone, **kwargs))
data = OrderedDict([(k, v.options(*args, **kwargs))
for k, v in self.data.items()])
return self.clone(data)

Expand Down Expand Up @@ -946,7 +946,7 @@ def opts(self, options=None, backend=None, clone=True, **kwargs):
return dmap


def options(self, options=None, backend=None, clone=True, **kwargs):
def options(self, *args, **kwargs):
"""
Applies options on an object or nested group of objects in a
flat format returning a new object with the options
Expand All @@ -968,17 +968,18 @@ def options(self, options=None, backend=None, clone=True, **kwargs):
Disabling clone will modify the object inplace.
"""
from ..util import Dynamic
clone = kwargs.get('clone', True)

obj = self if clone else self.clone()
dmap = Dynamic(obj, operation=lambda obj, **dynkwargs: obj.options(options, backend,
clone, **kwargs),
dmap = Dynamic(obj, operation=lambda obj, **dynkwargs: obj.options(*args, **kwargs),
streams=self.streams, link_inputs=True)
if not clone:
with util.disable_constant(self):
self.callback = dmap.callback
self.callback.inputs[:] = [obj]
obj.callback.inputs[:] = []
dmap = self
dmap.data = OrderedDict([(k, v.options(options, backend, **kwargs))
dmap.data = OrderedDict([(k, v.options(*args, **kwargs))
for k, v in self.data.items()])
return dmap

Expand Down
27 changes: 26 additions & 1 deletion holoviews/core/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class Config(param.ParameterizedFunction):
Switch to the default style options used up to (and including)
the HoloViews 1.7 release.""")

warn_options_call = param.Boolean(default=False, doc="""
warn_options_call = param.Boolean(default=True, doc="""
Whether to warn when the deprecated __call__ options syntax is
used (the opts method should now be used instead). It is
recommended that users switch this on to update any uses of
Expand Down Expand Up @@ -144,6 +144,31 @@ def default(self, obj):
return id(obj)


def merge_option_dicts(old_opts, new_opts):
"""
Update the old_opts option dictionary with the options defined in
new_opts. Instead of a shallow update as would be performed by calling
old_opts.update(new_opts), this updates the dictionaries of all option
types separately.

Given two dictionaries
old_opts = {'a': {'x': 'old', 'y': 'old'}}
and
new_opts = {'a': {'y': 'new', 'z': 'new'}, 'b': {'k': 'new'}}
this returns a dictionary
{'a': {'x': 'old', 'y': 'new', 'z': 'new'}, 'b': {'k': 'new'}}
"""
merged = dict(old_opts)

for option_type, options in new_opts.items():
if option_type not in merged:
merged[option_type] = {}

merged[option_type].update(options)

return merged


class periodic(Thread):
"""
Run a callback count times with a given period without blocking.
Expand Down
2 changes: 1 addition & 1 deletion holoviews/plotting/bokeh/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
Store.renderers['bokeh'] = BokehRenderer.instance()

if len(Store.renderers) == 1:
Store.current_backend = 'bokeh'
Store.set_current_backend('bokeh')

associations = {Overlay: OverlayPlot,
NdOverlay: OverlayPlot,
Expand Down
2 changes: 1 addition & 1 deletion holoviews/plotting/mpl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def get_color_cycle():
Store.renderers['matplotlib'] = MPLRenderer.instance()

if len(Store.renderers) == 1:
Store.current_backend = 'matplotlib'
Store.set_current_backend('matplotlib')

# Defines a wrapper around GridPlot and RasterGridPlot
# switching to RasterGridPlot if the plot only contains
Expand Down
2 changes: 1 addition & 1 deletion holoviews/plotting/plotly/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
Store.renderers['plotly'] = PlotlyRenderer.instance()

if len(Store.renderers) == 1:
Store.current_backend = 'plotly'
Store.set_current_backend('plotly')

Store.register({Points: PointPlot,
Scatter: PointPlot,
Expand Down
Loading