File: C:/Users/fred/anaconda3/Lib/site-packages/holoviews/tests/core/test_options.py
import contextlib
import pickle
from unittest import SkipTest
import numpy as np
import pytest
from holoviews import (
Curve,
DynamicMap,
Histogram,
Image,
Points,
Store,
opts,
plotting,
util,
)
from holoviews.core.options import (
Cycle,
OptionError,
Options,
OptionTree,
StoreOptions,
options_policy,
)
from holoviews.element.comparison import ComparisonTestCase
from holoviews.plotting import bokeh # noqa: F401
Options.skip_invalid = False
# Needed a backend to register backend and options
try:
from holoviews.plotting import mpl
except ImportError:
mpl = None
with contextlib.suppress(ImportError):
from holoviews.plotting import plotly # noqa : F401
class TestOptions(ComparisonTestCase):
def setUp(self):
self.original_option_groups = Options._option_groups
Options._option_groups = ['test']
super().setUp()
def tearDown(self):
Options._option_groups = self.original_option_groups
super().tearDown()
def test_options_init(self):
Options('test')
def test_options_valid_keywords1(self):
opts = Options('test', allowed_keywords=['kw1'], kw1='value')
self.assertEqual(opts.kwargs, {'kw1':'value'})
def test_options_valid_keywords2(self):
opts = Options('test', allowed_keywords=['kw1', 'kw2'], kw1='value')
self.assertEqual(opts.kwargs, {'kw1':'value'})
def test_options_valid_keywords3(self):
opts = Options('test', allowed_keywords=['kw1', 'kw2'], kw1='value1', kw2='value2')
self.assertEqual(opts.kwargs, {'kw1':'value1', 'kw2':'value2'})
def test_options_any_keywords3(self):
opts = Options('test', kw1='value1', kw2='value3')
self.assertEqual(opts.kwargs, {'kw1':'value1', 'kw2':'value3'})
def test_options_invalid_keywords1(self):
try:
Options('test', allowed_keywords=['kw1'], kw='value')
except OptionError as e:
self.assertEqual(str(e), "Invalid option 'kw', valid options are: ['kw1'].")
def test_options_invalid_keywords2(self):
try:
Options('test', allowed_keywords=['kw2'], kw2='value', kw3='value')
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):
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
self.assertEqual(opts, dict(kw2='value', kw3='value'))
def test_options_get_options_cyclic1(self):
opts = Options('test', allowed_keywords=['kw2', 'kw3'],
kw2='value', kw3='value')
for i in range(16):
self.assertEqual(opts[i], dict(kw2='value', kw3='value'))
def test_options_keys(self):
opts = Options('test', allowed_keywords=['kw3', 'kw2'],
kw2='value', kw3='value')
self.assertEqual(opts.keys(), ['kw2', 'kw3'])
def test_options_inherit(self):
original_kws = dict(kw2='value', kw3='value')
opts = Options('test', **original_kws)
new_kws = dict(kw4='val4', kw5='val5')
new_opts = opts(**new_kws)
self.assertEqual(new_opts.options, dict(original_kws, **new_kws))
def test_options_inherit_invalid_keywords(self):
original_kws = dict(kw2='value', kw3='value')
opts = Options('test', allowed_keywords=['kw3', 'kw2'], **original_kws)
new_kws = dict(kw4='val4', kw5='val5')
try:
opts(**new_kws)
except OptionError as e:
self.assertEqual(str(e), "Invalid option 'kw4', valid options are: ['kw2', 'kw3'].")
class TestCycle(ComparisonTestCase):
def setUp(self):
self.original_option_groups = Options._option_groups
Options._option_groups = ['test']
super().setUp()
def tearDown(self):
Options._option_groups = self.original_option_groups
super().tearDown()
def test_cycle_init(self):
Cycle(values=['a', 'b', 'c'])
Cycle(values=[1, 2, 3])
def test_cycle_expansion(self):
cycle1 = Cycle(values=['a', 'b', 'c'])
cycle2 = Cycle(values=[1, 2, 3])
opts = Options('test', one=cycle1, two=cycle2)
self.assertEqual(opts[0], {'one': 'a', 'two': 1})
self.assertEqual(opts[1], {'one': 'b', 'two': 2})
self.assertEqual(opts[2], {'one': 'c', 'two': 3})
self.assertEqual(opts[3], {'one': 'a', 'two': 1})
self.assertEqual(opts[4], {'one': 'b', 'two': 2})
self.assertEqual(opts[5], {'one': 'c', 'two': 3})
def test_cycle_expansion_unequal(self):
cycle1 = Cycle(values=['a', 'b', 'c', 'd'])
cycle2 = Cycle(values=[1, 2, 3])
opts = Options('test', one=cycle1, two=cycle2)
self.assertEqual(opts[0], {'one': 'a', 'two': 1})
self.assertEqual(opts[1], {'one': 'b', 'two': 2})
self.assertEqual(opts[2], {'one': 'c', 'two': 3})
self.assertEqual(opts[3], {'one': 'd', 'two': 1})
self.assertEqual(opts[4], {'one': 'a', 'two': 2})
self.assertEqual(opts[5], {'one': 'b', 'two': 3})
def test_cycle_slice(self):
cycle1 = Cycle(values=['a', 'b', 'c'])[2]
cycle2 = Cycle(values=[1, 2, 3])
opts = Options('test', one=cycle1, two=cycle2)
self.assertEqual(opts[0], {'one': 'a', 'two': 1})
self.assertEqual(opts[1], {'one': 'b', 'two': 2})
self.assertEqual(opts[2], {'one': 'a', 'two': 3})
self.assertEqual(opts[3], {'one': 'b', 'two': 1})
def test_cyclic_property_true(self):
cycle1 = Cycle(values=['a', 'b', 'c'])
opts = Options('test', one=cycle1, two='two')
self.assertEqual(opts.cyclic, True)
def test_cyclic_property_false(self):
opts = Options('test', one='one', two='two')
self.assertEqual(opts.cyclic, False)
def test_options_property_disabled(self):
cycle1 = Cycle(values=['a', 'b', 'c'])
opts = Options('test', one=cycle1)
msg = r"The options property may only be used with non-cyclic Options\."
with pytest.raises(Exception, match=msg):
opts.options # noqa: B018
class TestOptionTree(ComparisonTestCase):
def setUp(self):
super().setUp()
self.original_option_groups = Options._option_groups[:]
Options._option_groups = ['group1', 'group2']
def tearDown(self):
Options._option_groups = self.original_option_groups
super().tearDown()
def test_optiontree_init_1(self):
OptionTree(groups=['group1', 'group2'])
def test_optiontree_init_2(self):
OptionTree(groups=['group1', 'group2'])
def test_optiontree_setter_getter(self):
options = OptionTree(groups=['group1', 'group2'])
opts = Options('group1', kw1='value')
options.MyType = opts
self.assertEqual(options.MyType['group1'], opts)
self.assertEqual(options.MyType['group1'].options, {'kw1':'value'})
def test_optiontree_dict_setter_getter(self):
options = OptionTree(groups=['group1', 'group2'])
opts1 = Options(kw1='value1')
opts2 = Options(kw2='value2')
options.MyType = {'group1':opts1, 'group2':opts2}
self.assertEqual(options.MyType['group1'], opts1)
self.assertEqual(options.MyType['group1'].options, {'kw1':'value1'})
self.assertEqual(options.MyType['group2'], opts2)
self.assertEqual(options.MyType['group2'].options, {'kw2':'value2'})
def test_optiontree_inheritance(self):
options = OptionTree(groups=['group1', 'group2'])
opts1 = Options(kw1='value1')
opts2 = Options(kw2='value2')
options.MyType = {'group1':opts1, 'group2':opts2}
opts3 = Options(kw3='value3')
opts4 = Options(kw4='value4')
options.MyType.Child = {'group1':opts3, 'group2':opts4}
self.assertEqual(options.MyType.Child.options('group1').kwargs,
{'kw1':'value1', 'kw3':'value3'})
self.assertEqual(options.MyType.Child.options('group2').kwargs,
{'kw2':'value2', 'kw4':'value4'})
def test_optiontree_inheritance_flipped(self):
"""
Tests for ordering problems manifested in issue #93
"""
options = OptionTree(groups=['group1', 'group2'])
opts3 = Options(kw3='value3')
opts4 = Options(kw4='value4')
options.MyType.Child = {'group1':opts3, 'group2':opts4}
opts1 = Options(kw1='value1')
opts2 = Options(kw2='value2')
options.MyType = {'group1':opts1, 'group2':opts2}
self.assertEqual(options.MyType.Child.options('group1').kwargs,
{'kw1':'value1', 'kw3':'value3'})
self.assertEqual(options.MyType.Child.options('group2').kwargs,
{'kw2':'value2', 'kw4':'value4'})
class TestStoreInheritanceDynamic(ComparisonTestCase):
"""
Tests to prevent regression after fix in PR #646
"""
def setUp(self):
if mpl is None:
raise SkipTest("Matplotlib required to test Store inheritance")
self.backend = 'matplotlib'
Store.set_current_backend(self.backend)
options = Store.options()
self.store_copy = OptionTree(sorted(options.items()),
groups=Options._option_groups,
backend=options.backend)
super().setUp()
def tearDown(self):
Store.options(val=self.store_copy)
super().tearDown()
def initialize_option_tree(self):
Store.options(val=OptionTree(groups=['plot', 'style']))
options = Store.options()
options.Image = Options('style', cmap='hot', interpolation='nearest')
return options
def test_empty_group(self):
"Test to prevent regression of issue fixed in #5131"
ls = np.linspace(0, 10, 200)
xx, yy = np.meshgrid(ls, ls)
util.render(Image(np.sin(xx)*np.cos(yy), group="").opts(cmap="greys"))
def test_merge_keywords(self):
options = self.initialize_option_tree()
options.Image = Options('style', clims=(0, 0.5))
expected = {'clims': (0, 0.5), 'cmap': 'hot', 'interpolation': 'nearest'}
direct_kws = options.Image.groups['style'].kwargs
inherited_kws = options.Image.options('style').kwargs
self.assertEqual(direct_kws, expected)
self.assertEqual(inherited_kws, expected)
def test_merge_keywords_disabled(self):
options = self.initialize_option_tree()
options.Image = Options('style', clims=(0, 0.5), merge_keywords=False)
expected = {'clims': (0, 0.5)}
direct_kws = options.Image.groups['style'].kwargs
inherited_kws = options.Image.options('style').kwargs
self.assertEqual(direct_kws, expected)
self.assertEqual(inherited_kws, expected)
def test_specification_general_to_specific_group(self):
"""
Test order of specification starting with general and moving
to specific
"""
options = self.initialize_option_tree()
obj = Image(np.random.rand(10,10), group='SomeGroup')
options.Image = Options('style', cmap='viridis')
options.Image.SomeGroup = Options('style', alpha=0.2)
expected = {'alpha': 0.2, 'cmap': 'viridis', 'interpolation': 'nearest'}
lookup = Store.lookup_options('matplotlib', obj, 'style')
self.assertEqual(lookup.kwargs, expected)
# Check the tree is structured as expected
node1 = options.Image.groups['style']
node2 = options.Image.SomeGroup.groups['style']
self.assertEqual(node1.kwargs, {'cmap': 'viridis', 'interpolation': 'nearest'})
self.assertEqual(node2.kwargs, {'alpha': 0.2})
def test_specification_general_to_specific_group_and_label(self):
"""
Test order of specification starting with general and moving
to specific
"""
options = self.initialize_option_tree()
obj = Image(np.random.rand(10,10), group='SomeGroup', label='SomeLabel')
options.Image = Options('style', cmap='viridis')
options.Image.SomeGroup.SomeLabel = Options('style', alpha=0.2)
expected = {'alpha': 0.2, 'cmap': 'viridis', 'interpolation': 'nearest'}
lookup = Store.lookup_options('matplotlib', obj, 'style')
self.assertEqual(lookup.kwargs, expected)
# Check the tree is structured as expected
node1 = options.Image.groups['style']
node2 = options.Image.SomeGroup.SomeLabel.groups['style']
self.assertEqual(node1.kwargs, {'cmap': 'viridis', 'interpolation': 'nearest'})
self.assertEqual(node2.kwargs, {'alpha': 0.2})
def test_specification_specific_to_general_group(self):
"""
Test order of specification starting with a specific option and
then specifying a general one
"""
options = self.initialize_option_tree()
options.Image.SomeGroup = Options('style', alpha=0.2)
obj = Image(np.random.rand(10,10), group='SomeGroup')
options.Image = Options('style', cmap='viridis')
expected = {'alpha': 0.2, 'cmap': 'viridis', 'interpolation': 'nearest'}
lookup = Store.lookup_options('matplotlib', obj, 'style')
self.assertEqual(lookup.kwargs, expected)
# Check the tree is structured as expected
node1 = options.Image.groups['style']
node2 = options.Image.SomeGroup.groups['style']
self.assertEqual(node1.kwargs, {'cmap': 'viridis', 'interpolation': 'nearest'})
self.assertEqual(node2.kwargs, {'alpha': 0.2})
def test_specification_specific_to_general_group_and_label(self):
"""
Test order of specification starting with general and moving
to specific
"""
options = self.initialize_option_tree()
options.Image.SomeGroup.SomeLabel = Options('style', alpha=0.2)
obj = Image(np.random.rand(10,10), group='SomeGroup', label='SomeLabel')
options.Image = Options('style', cmap='viridis')
expected = {'alpha': 0.2, 'cmap': 'viridis', 'interpolation': 'nearest'}
lookup = Store.lookup_options('matplotlib', obj, 'style')
self.assertEqual(lookup.kwargs, expected)
# Check the tree is structured as expected
node1 = options.Image.groups['style']
node2 = options.Image.SomeGroup.SomeLabel.groups['style']
self.assertEqual(node1.kwargs, {'cmap': 'viridis', 'interpolation': 'nearest'})
self.assertEqual(node2.kwargs, {'alpha': 0.2})
def test_backend_opts_to_default_inheritance(self):
"""
Checks customs inheritance backs off to default tree correctly
using .opts.
"""
options = self.initialize_option_tree()
options.Image.A.B = Options('style', alpha=0.2)
obj = Image(np.random.rand(10, 10), group='A', label='B')
expected_obj = {'alpha': 0.2, 'cmap': 'hot', 'interpolation': 'nearest'}
obj_lookup = Store.lookup_options('matplotlib', obj, 'style')
self.assertEqual(obj_lookup.kwargs, expected_obj)
# Customize this particular object
custom_obj = opts.apply_groups(obj, style=dict(clims=(0, 0.5)))
expected_custom_obj = dict(clims=(0,0.5), **expected_obj)
custom_obj_lookup = Store.lookup_options('matplotlib', custom_obj, 'style')
self.assertEqual(custom_obj_lookup.kwargs, expected_custom_obj)
def test_custom_magic_to_default_inheritance(self):
"""
Checks customs inheritance backs off to default tree correctly
simulating the %%opts cell magic.
"""
options = self.initialize_option_tree()
options.Image.A.B = Options('style', alpha=0.2)
obj = Image(np.random.rand(10, 10), group='A', label='B')
# Before customizing...
expected_obj = {'alpha': 0.2, 'cmap': 'hot', 'interpolation': 'nearest'}
obj_lookup = Store.lookup_options('matplotlib', obj, 'style')
self.assertEqual(obj_lookup.kwargs, expected_obj)
custom_tree = {0: OptionTree(groups=Options._option_groups,
style={'Image' : dict(clims=(0, 0.5))})}
Store._custom_options['matplotlib'] = custom_tree
obj.id = 0 # Manually set the id to point to the tree above
# Customize this particular object
expected_custom_obj = dict(clims=(0,0.5), **expected_obj)
custom_obj_lookup = Store.lookup_options('matplotlib', obj, 'style')
self.assertEqual(custom_obj_lookup.kwargs, expected_custom_obj)
class TestStoreInheritance(ComparisonTestCase):
"""
Tests to prevent regression after fix in 71c1f3a that resolves
issue #43
"""
def setUp(self):
if mpl is None:
raise SkipTest("Matplotlib required to test Store inheritance")
self.backend = 'matplotlib'
Store.set_current_backend(self.backend)
self.store_copy = OptionTree(sorted(Store.options().items()),
groups=Options._option_groups)
Store.options(val=OptionTree(
groups=['plot', 'style'], backend=self.backend
))
options = Store.options()
self.default_plot = dict(plot1='plot1', plot2='plot2')
options.Histogram = Options('plot', **self.default_plot)
self.default_style = dict(style1='style1', style2='style2')
options.Histogram = Options('style', **self.default_style)
data = [np.random.normal() for i in range(10000)]
frequencies, edges = np.histogram(data, 20)
self.hist = Histogram((edges, frequencies))
super().setUp()
def tearDown(self):
Store.options(val=self.store_copy)
super().tearDown()
def lookup_options(self, obj, group):
return Store.lookup_options(self.backend, obj, group)
def test_original_style_options(self):
self.assertEqual(self.lookup_options(self.hist, 'style').options,
self.default_style)
def test_original_plot_options(self):
self.assertEqual(self.lookup_options(self.hist, 'plot').options,
self.default_plot)
def test_plot_inheritance_addition(self):
"Adding an element"
hist2 = opts.apply_groups(self.hist, plot={'plot3':'plot3'})
self.assertEqual(self.lookup_options(hist2, 'plot').options,
dict(plot1='plot1', plot2='plot2', plot3='plot3'))
# Check style works as expected
self.assertEqual(self.lookup_options(hist2, 'style').options, self.default_style)
def test_plot_inheritance_override(self):
"Overriding an element"
hist2 = opts.apply_groups(self.hist, plot={'plot1':'plot_child'})
self.assertEqual(self.lookup_options(hist2, 'plot').options,
dict(plot1='plot_child', plot2='plot2'))
# Check style works as expected
self.assertEqual(self.lookup_options(hist2, 'style').options, self.default_style)
def test_style_inheritance_addition(self):
"Adding an element"
hist2 = opts.apply_groups(self.hist, style={'style3':'style3'})
self.assertEqual(self.lookup_options(hist2, 'style').options,
dict(style1='style1', style2='style2', style3='style3'))
# Check plot options works as expected
self.assertEqual(self.lookup_options(hist2, 'plot').options, self.default_plot)
def test_style_inheritance_override(self):
"Overriding an element"
hist2 = opts.apply_groups(self.hist, style={'style1':'style_child'})
self.assertEqual(self.lookup_options(hist2, 'style').options,
dict(style1='style_child', style2='style2'))
# Check plot options works as expected
self.assertEqual(self.lookup_options(hist2, 'plot').options, self.default_plot)
def test_style_transfer(self):
hist = opts.apply_groups(self.hist, style={'style1':'style_child'})
hist2 = self.hist.opts()
opts_kwargs = Store.lookup_options('matplotlib', hist2, 'style').kwargs
self.assertEqual(opts_kwargs, {'style1': 'style1', 'style2': 'style2'})
Store.transfer_options(hist, hist2, 'matplotlib')
opts_kwargs = Store.lookup_options('matplotlib', hist2, 'style').kwargs
self.assertEqual(opts_kwargs, {'style1': 'style_child', 'style2': 'style2'})
class TestOptionsMethod(ComparisonTestCase):
def setUp(self):
if mpl is None:
raise SkipTest("Matplotlib required to test Store inheritance")
self.backend = 'matplotlib'
Store.set_current_backend(self.backend)
self.store_copy = OptionTree(sorted(Store.options().items()),
groups=Options._option_groups)
super().setUp()
def tearDown(self):
Store.options(val=self.store_copy)
super().tearDown()
def lookup_options(self, obj, group):
return Store.lookup_options(self.backend, obj, group)
def test_plot_options_keywords(self):
im = Image(np.random.rand(10,10))
styled_im = im.options(interpolation='nearest', cmap='jet')
self.assertEqual(self.lookup_options(im, 'plot').options, {})
self.assertEqual(self.lookup_options(styled_im, 'style').options,
dict(cmap='jet', interpolation='nearest'))
def test_plot_options_one_object(self):
im = Image(np.random.rand(10,10))
imopts = opts.Image(interpolation='nearest', cmap='jet')
styled_im = im.options(imopts)
self.assertEqual(self.lookup_options(im, 'plot').options, {})
self.assertEqual(self.lookup_options(styled_im, 'style').options,
dict(cmap='jet', interpolation='nearest'))
def test_plot_options_two_object(self):
im = Image(np.random.rand(10,10))
imopts1 = opts.Image(interpolation='nearest')
imopts2 = opts.Image(cmap='hsv')
styled_im = im.options(imopts1,imopts2)
self.assertEqual(self.lookup_options(im, 'plot').options, {})
self.assertEqual(self.lookup_options(styled_im, 'style').options,
dict(cmap='hsv', interpolation='nearest'))
def test_plot_options_object_list(self):
im = Image(np.random.rand(10,10))
imopts1 = opts.Image(interpolation='nearest')
imopts2 = opts.Image(cmap='summer')
styled_im = im.options([imopts1,imopts2])
self.assertEqual(self.lookup_options(im, 'plot').options, {})
self.assertEqual(self.lookup_options(styled_im, 'style').options,
dict(cmap='summer', interpolation='nearest'))
class TestOptsMethod(ComparisonTestCase):
def setUp(self):
if mpl is None:
raise SkipTest("Matplotlib required to test Store inheritance")
self.backend = 'matplotlib'
Store.set_current_backend(self.backend)
self.store_copy = OptionTree(sorted(Store.options().items()),
groups=Options._option_groups)
super().setUp()
def tearDown(self):
Store.options(val=self.store_copy)
super().tearDown()
def lookup_options(self, obj, group):
return Store.lookup_options(self.backend, obj, group)
def test_simple_clone_disabled(self):
im = Image(np.random.rand(10,10))
styled_im = im.opts(interpolation='nearest', cmap='jet', clone=False)
self.assertEqual(self.lookup_options(im, 'plot').options, {})
self.assertEqual(self.lookup_options(styled_im, 'plot').options, {})
assert styled_im is im
self.assertEqual(self.lookup_options(im, 'style').options,
{'cmap': 'jet', 'interpolation': 'nearest'})
def test_simple_opts_clone_enabled(self):
im = Image(np.random.rand(10,10))
styled_im = im.opts(interpolation='nearest', cmap='jet', clone=True)
self.assertEqual(self.lookup_options(im, 'plot').options, {})
self.assertEqual(self.lookup_options(styled_im, 'plot').options, {})
assert styled_im is not im
im_lookup = self.lookup_options(im, 'style').options
self.assertEqual(im_lookup['cmap'] == 'jet', False)
styled_im_lookup = self.lookup_options(styled_im, 'style').options
self.assertEqual(styled_im_lookup['cmap'] == 'jet', True)
def test_opts_method_with_utility(self):
im = Image(np.random.rand(10,10))
imopts = opts.Image(cmap='Blues')
styled_im = im.opts(imopts)
assert styled_im is im
self.assertEqual(self.lookup_options(im, 'style').options,
{'cmap': 'Blues', 'interpolation': 'nearest'})
def test_opts_method_dynamicmap_grouped(self):
dmap = DynamicMap(lambda X: Curve([1, 2, X]),
kdims=['X']).redim.range(X=(0, 3))
retval = dmap.opts(padding=1, clone=True)
assert retval is not dmap
self.assertEqual(self.lookup_options(retval[0], 'plot').options,
{'padding':1})
def test_opts_clear(self):
im = Image(np.random.rand(10,10))
styled_im = opts.apply_groups(im, style=dict(cmap='jet', interpolation='nearest',
option1='A', option2='B'), clone=False)
self.assertEqual(self.lookup_options(im, 'style').options,
{'cmap': 'jet', 'interpolation': 'nearest',
'option1':'A', 'option2':'B'})
assert styled_im is im
cleared = im.opts.clear()
assert cleared is im
cleared_options = self.lookup_options(cleared, 'style').options
self.assertEqual(not any(k in ['option1', 'option2']
for k in cleared_options.keys()), True)
def test_opts_clear_clone(self):
im = Image(np.random.rand(10,10))
styled_im = opts.apply_groups(im, style=dict(cmap='jet', interpolation='nearest',
option1='A', option2='B'), clone=False)
self.assertEqual(self.lookup_options(im, 'style').options,
{'cmap': 'jet', 'interpolation': 'nearest',
'option1':'A', 'option2':'B'})
assert styled_im is im
cleared = im.opts.clear(clone=True)
assert cleared is not im
self.assertEqual(self.lookup_options(im, 'style').options,
{'cmap': 'jet', 'interpolation': 'nearest',
'option1':'A', 'option2':'B'})
cleared_options = self.lookup_options(cleared, 'style').options
self.assertEqual(not any(k in ['option1', 'option2']
for k in cleared_options.keys()), True)
class TestOptionTreeFind(ComparisonTestCase):
def setUp(self):
self.original_option_groups = Options._option_groups[:]
Options._option_groups = ['group']
options = OptionTree(groups=['group'])
self.opts1 = Options('group', kw1='value1')
self.opts2 = Options('group', kw2='value2')
self.opts3 = Options('group', kw3='value3')
self.opts4 = Options('group', kw4='value4')
self.opts5 = Options('group', kw5='value5')
self.opts6 = Options('group', kw6='value6')
options.MyType = self.opts1
options.XType = self.opts2
options.MyType.Foo = self.opts3
options.MyType.Bar = self.opts4
options.XType.Foo = self.opts5
options.XType.Bar = self.opts6
self.options = options
self.original_options=Store.options()
Store.options(val=OptionTree(groups=['group']))
def tearDown(self):
Options._option_groups = self.original_option_groups
Store.options(val=self.original_options)
def test_optiontree_find1(self):
self.assertEqual(self.options.find('MyType').options('group').options,
dict(kw1='value1'))
def test_optiontree_find2(self):
self.assertEqual(self.options.find('XType').options('group').options,
dict(kw2='value2'))
def test_optiontree_find3(self):
self.assertEqual(self.options.find('MyType.Foo').options('group').options,
dict(kw1='value1', kw3='value3'))
def test_optiontree_find4(self):
self.assertEqual(self.options.find('MyType.Bar').options('group').options,
dict(kw1='value1', kw4='value4'))
def test_optiontree_find5(self):
self.assertEqual(self.options.find('XType.Foo').options('group').options,
dict(kw2='value2', kw5='value5'))
def test_optiontree_find6(self):
self.assertEqual(self.options.find('XType.Bar').options('group').options,
dict(kw2='value2', kw6='value6'))
def test_optiontree_find_mismatch1(self):
self.assertEqual(self.options.find('MyType.Baz').options('group').options,
dict(kw1='value1'))
def test_optiontree_find_mismatch2(self):
self.assertEqual(self.options.find('XType.Baz').options('group').options,
dict(kw2='value2'))
def test_optiontree_find_mismatch3(self):
self.assertEqual(self.options.find('Baz').options('group').options, {})
def test_optiontree_find_mismatch4(self):
self.assertEqual(self.options.find('Baz.Baz').options('group').options, {})
class TestCrossBackendOptions(ComparisonTestCase):
"""
Test the style system can style a single object across backends.
"""
def setUp(self):
if mpl is None:
raise SkipTest("Matplotlib required to test Store inheritance")
# Some tests require that plotly isn't loaded
self.plotly_options = Store._options.pop('plotly', None)
self.store_mpl = OptionTree(
sorted(Store.options(backend='matplotlib').items()),
groups=Options._option_groups,
backend='matplotlib'
)
self.store_bokeh = OptionTree(
sorted(Store.options(backend='bokeh').items()),
groups=Options._option_groups,
backend='bokeh'
)
self.clear_options()
super().setUp()
def clear_options(self):
# Clear global options..
Store.options(val=OptionTree(groups=['plot', 'style'], backend='matplotlib'),
backend='matplotlib')
Store.options(val=OptionTree(groups=['plot', 'style'], backend='bokeh'),
backend='bokeh')
# ... and custom options
Store.custom_options({}, backend='matplotlib')
Store.custom_options({}, backend='bokeh')
def tearDown(self):
Store.options(val=self.store_mpl, backend='matplotlib')
Store.options(val=self.store_bokeh, backend='bokeh')
Store.current_backend = 'matplotlib'
if self.plotly_options is not None:
Store._options['plotly'] = self.plotly_options
super().tearDown()
def test_mpl_bokeh_mpl(self):
img = Image(np.random.rand(10,10))
# Use blue in matplotlib
Store.current_backend = 'matplotlib'
StoreOptions.set_options(img, style={'Image':{'cmap':'Blues'}})
mpl_opts = Store.lookup_options('matplotlib', img, 'style').options
self.assertEqual(mpl_opts, {'cmap':'Blues'})
# Use purple in bokeh
Store.current_backend = 'bokeh'
StoreOptions.set_options(img, style={'Image':{'cmap':'Purple'}})
bokeh_opts = Store.lookup_options('bokeh', img, 'style').options
self.assertEqual(bokeh_opts, {'cmap':'Purple'})
# Check it is still blue in matplotlib...
Store.current_backend = 'matplotlib'
mpl_opts = Store.lookup_options('matplotlib', img, 'style').options
self.assertEqual(mpl_opts, {'cmap':'Blues'})
# And purple in bokeh..
Store.current_backend = 'bokeh'
bokeh_opts = Store.lookup_options('bokeh', img, 'style').options
self.assertEqual(bokeh_opts, {'cmap':'Purple'})
def test_mpl_bokeh_offset_mpl(self):
img = Image(np.random.rand(10,10))
# Use blue in matplotlib
Store.current_backend = 'matplotlib'
StoreOptions.set_options(img, style={'Image':{'cmap':'Blues'}})
mpl_opts = Store.lookup_options('matplotlib', img, 'style').options
self.assertEqual(mpl_opts, {'cmap':'Blues'})
# Switch to bokeh and style a random object...
Store.current_backend = 'bokeh'
img2 = Image(np.random.rand(10,10))
StoreOptions.set_options(img2, style={'Image':{'cmap':'Reds'}})
img2_opts = Store.lookup_options('bokeh', img2, 'style').options
self.assertEqual(img2_opts, {'cmap':'Reds'})
# Use purple in bokeh on the object...
StoreOptions.set_options(img, style={'Image':{'cmap':'Purple'}})
bokeh_opts = Store.lookup_options('bokeh', img, 'style').options
self.assertEqual(bokeh_opts, {'cmap':'Purple'})
# Check it is still blue in matplotlib...
Store.current_backend = 'matplotlib'
mpl_opts = Store.lookup_options('matplotlib', img, 'style').options
self.assertEqual(mpl_opts, {'cmap':'Blues'})
# And purple in bokeh..
Store.current_backend = 'bokeh'
bokeh_opts = Store.lookup_options('bokeh', img, 'style').options
self.assertEqual(bokeh_opts, {'cmap':'Purple'})
def test_builder_backend_switch_signature(self):
Store.options(val=self.store_mpl, backend='matplotlib')
Store.options(val=self.store_bokeh, backend='bokeh')
Store.set_current_backend('bokeh')
self.assertEqual(opts.Curve.__signature__ is not None, True)
sigkeys = opts.Curve.__signature__.parameters
self.assertEqual('color' in sigkeys, True)
self.assertEqual('line_width' in sigkeys, True)
Store.set_current_backend('matplotlib')
self.assertEqual(opts.Curve.__signature__ is not None, True)
sigkeys = opts.Curve.__signature__.parameters
self.assertEqual('color' in sigkeys, True)
self.assertEqual('linewidth' in sigkeys, True)
def test_builder_cross_backend_validation(self):
Store.options(val=self.store_mpl, backend='matplotlib')
Store.options(val=self.store_bokeh, backend='bokeh')
Store.set_current_backend('bokeh')
opts.Curve(line_dash='dotted') # Bokeh keyword
opts.Curve(linewidth=10) # MPL keyword
err = (
"In opts.Curve(...), keywords supplied are mixed across backends. "
"Keyword(s) 'linewidth' are invalid for bokeh, "
"'line_dash' are invalid for matplotlib"
)
with pytest.raises(ValueError) as excinfo:
opts.Curve(linewidth=10, line_dash='dotted') # Bokeh and MPL
assert err in str(excinfo.value)
# Non-existent keyword across backends (bokeh active)
err = ("In opts.Curve(...), unexpected option 'foobar' for Curve type "
"across all extensions. Similar options for current "
"extension ('bokeh') are: ['toolbar'].")
with pytest.raises(ValueError) as excinfo:
opts.Curve(foobar=3)
assert err in str(excinfo.value)
# Non-existent keyword across backends (matplotlib active)
Store.set_current_backend('matplotlib')
err = ("In opts.Curve(...), unexpected option 'foobar' for Curve "
"type across all extensions. No similar options found.")
with pytest.raises(ValueError) as excinfo:
opts.Curve(foobar=3)
assert err in str(excinfo.value)
class TestLookupOptions(ComparisonTestCase):
def test_lookup_options_honors_backend(self):
points = Points([[1, 2], [3, 4]])
try:
import holoviews.plotting.bokeh
import holoviews.plotting.mpl
import holoviews.plotting.plotly # noqa
except ImportError:
raise SkipTest("Matplotlib or Plotly not installed")
backends = Store.loaded_backends()
# Lookup points style options with matplotlib and plotly backends while current
# backend is bokeh
if 'bokeh' in backends:
Store.set_current_backend('bokeh')
if 'matplotlib' in backends:
options_matplotlib = Store.lookup_options("matplotlib", points, "style")
if 'plotly' in backends:
options_plotly = Store.lookup_options("plotly", points, "style")
# Lookup points style options with bokeh backend while current
# backend is matplotlib
if 'matplotlib' in backends:
Store.set_current_backend('matplotlib')
if 'bokeh' in backends:
options_bokeh = Store.lookup_options("bokeh", points, "style")
# Check matplotlib style options
if 'matplotlib' in backends:
for opt in ["cmap", "color", "marker"]:
self.assertIn(opt, options_matplotlib.keys())
self.assertNotIn("muted_alpha", options_matplotlib.keys())
# Check bokeh style options
if 'bokeh' in backends:
for opt in ["cmap", "color", "muted_alpha", "size"]:
self.assertIn(opt, options_bokeh.keys())
# Check plotly style options
if 'plotly' in backends:
for opt in ["color"]:
self.assertIn(opt, options_plotly.keys())
self.assertNotIn("muted_alpha", options_matplotlib.keys())
@pytest.mark.usefixtures("bokeh_backend")
class TestCrossBackendOptionSpecification(ComparisonTestCase):
"""
Test the style system can style a single object across backends.
"""
def setUp(self):
if mpl is None:
raise SkipTest("Matplotlib required to test Store inheritance")
# Some tests require that plotly isn't loaded
self.plotly_options = Store._options.pop('plotly', None)
self.store_mpl = OptionTree(
sorted(Store.options(backend='matplotlib').items()),
groups=Options._option_groups, backend='matplotlib'
)
self.store_bokeh = OptionTree(
sorted(Store.options(backend='bokeh').items()),
groups=Options._option_groups, backend='bokeh'
)
super().setUp()
def tearDown(self):
Store.options(val=self.store_mpl, backend='matplotlib')
Store.options(val=self.store_bokeh, backend='bokeh')
Store.current_backend = 'matplotlib'
if self.plotly_options is not None:
Store._options['plotly'] = self.plotly_options
super().tearDown()
def assert_output_options_group_empty(self, obj):
mpl_output_lookup = Store.lookup_options('matplotlib', obj, 'output').options
self.assertEqual(mpl_output_lookup, {})
bokeh_output_lookup = Store.lookup_options('bokeh', obj, 'output').options
self.assertEqual(bokeh_output_lookup, {})
def test_mpl_bokeh_mpl_via_option_objects_opts_method(self):
img = Image(np.random.rand(10,10))
mpl_opts = Options('Image', cmap='Blues', backend='matplotlib')
bokeh_opts = Options('Image', cmap='Purple', backend='bokeh')
self.assertEqual(mpl_opts.kwargs['backend'], 'matplotlib')
self.assertEqual(bokeh_opts.kwargs['backend'], 'bokeh')
img.opts(mpl_opts, bokeh_opts)
mpl_lookup = Store.lookup_options('matplotlib', img, 'style').options
self.assertEqual(mpl_lookup['cmap'], 'Blues')
bokeh_lookup = Store.lookup_options('bokeh', img, 'style').options
self.assertEqual(bokeh_lookup['cmap'], 'Purple')
self.assert_output_options_group_empty(img)
def test_mpl_bokeh_mpl_via_builders_opts_method(self):
img = Image(np.random.rand(10,10))
mpl_opts = opts.Image(cmap='Blues', backend='matplotlib')
bokeh_opts = opts.Image(cmap='Purple', backend='bokeh')
self.assertEqual(mpl_opts.kwargs['backend'], 'matplotlib')
self.assertEqual(bokeh_opts.kwargs['backend'], 'bokeh')
img.opts(mpl_opts, bokeh_opts)
mpl_lookup = Store.lookup_options('matplotlib', img, 'style').options
self.assertEqual(mpl_lookup['cmap'], 'Blues')
bokeh_lookup = Store.lookup_options('bokeh', img, 'style').options
self.assertEqual(bokeh_lookup['cmap'], 'Purple')
self.assert_output_options_group_empty(img)
def test_mpl_bokeh_mpl_via_dict_backend_keyword(self):
curve = Curve([1,2,3])
styled_mpl = curve.opts({'Curve': dict(color='red')}, backend='matplotlib')
styled = styled_mpl.opts({'Curve': dict(color='green')}, backend='bokeh')
mpl_lookup = Store.lookup_options('matplotlib', styled, 'style')
self.assertEqual(mpl_lookup.kwargs['color'], 'red')
bokeh_lookup = Store.lookup_options('bokeh', styled, 'style')
self.assertEqual(bokeh_lookup.kwargs['color'], 'green')
def test_mpl_bokeh_mpl_via_builders_opts_method_implicit_backend(self):
img = Image(np.random.rand(10,10))
Store.set_current_backend('matplotlib')
mpl_opts = opts.Image(cmap='Blues')
bokeh_opts = opts.Image(cmap='Purple', backend='bokeh')
self.assertEqual('backend' not in mpl_opts.kwargs, True)
self.assertEqual(bokeh_opts.kwargs['backend'], 'bokeh')
img.opts(mpl_opts, bokeh_opts)
mpl_lookup = Store.lookup_options('matplotlib', img, 'style').options
self.assertEqual(mpl_lookup['cmap'], 'Blues')
bokeh_lookup = Store.lookup_options('bokeh', img, 'style').options
self.assertEqual(bokeh_lookup['cmap'], 'Purple')
self.assert_output_options_group_empty(img)
def test_mpl_bokeh_mpl_via_builders_opts_method_literal_implicit_backend(self):
img = Image(np.random.rand(10,10))
curve = Curve([1,2,3])
overlay = img * curve
Store.set_current_backend('matplotlib')
literal = {'Curve': dict(color='orange'),
'Image': dict(cmap='jet', backend='bokeh'),
}
styled = overlay.opts(literal)
mpl_curve_lookup = Store.lookup_options('matplotlib', styled.Curve.I, 'style')
self.assertEqual(mpl_curve_lookup.kwargs['color'], 'orange')
mpl_img_lookup = Store.lookup_options('matplotlib', styled.Image.I, 'style')
self.assertNotEqual(mpl_img_lookup.kwargs['cmap'], 'jet')
bokeh_curve_lookup = Store.lookup_options('bokeh', styled.Curve.I, 'style')
self.assertNotEqual(bokeh_curve_lookup.kwargs['color'], 'orange')
bokeh_img_lookup = Store.lookup_options('bokeh', styled.Image.I, 'style')
self.assertEqual(bokeh_img_lookup.kwargs['cmap'], 'jet')
def test_mpl_bokeh_mpl_via_builders_opts_method_literal_explicit_backend(self):
img = Image(np.random.rand(10,10))
curve = Curve([1,2,3])
overlay = img * curve
Store.set_current_backend('matplotlib')
literal = {'Curve': dict(color='orange', backend='matplotlib'),
'Image': dict(cmap='jet', backend='bokeh')
}
styled = overlay.opts(literal)
mpl_curve_lookup = Store.lookup_options('matplotlib', styled.Curve.I, 'style')
self.assertEqual(mpl_curve_lookup.kwargs['color'], 'orange')
mpl_img_lookup = Store.lookup_options('matplotlib', styled.Image.I, 'style')
self.assertNotEqual(mpl_img_lookup.kwargs['cmap'], 'jet')
bokeh_curve_lookup = Store.lookup_options('bokeh', styled.Curve.I, 'style')
self.assertNotEqual(bokeh_curve_lookup.kwargs['color'], 'orange')
bokeh_img_lookup = Store.lookup_options('bokeh', styled.Image.I, 'style')
self.assertEqual(bokeh_img_lookup.kwargs['cmap'], 'jet')
def test_mpl_bokeh_output_options_group_expandable(self):
original_allowed_kws = Options._output_allowed_kws[:]
Options._output_allowed_kws = ['backend', 'file_format_example']
# Re-register
Store.register({Curve: plotting.mpl.CurvePlot}, 'matplotlib')
Store.register({Curve: plotting.bokeh.CurvePlot}, 'bokeh')
curve_bk = Options('Curve', backend='bokeh', color='blue')
curve_mpl = Options('Curve', backend='matplotlib', color='red',
file_format_example='SVG')
c = Curve([1,2,3])
styled = c.opts(curve_bk, curve_mpl)
self.assertEqual(Store.lookup_options('matplotlib', styled, 'output').kwargs,
{'backend':'matplotlib', 'file_format_example':'SVG'})
self.assertEqual(Store.lookup_options('bokeh', styled, 'output').kwargs,
{})
Options._output_allowed_kws = original_allowed_kws
class TestCrossBackendOptionPickling(TestCrossBackendOptions):
def setUp(self):
super().setUp()
self.raw = Image(np.random.rand(10,10))
Store.current_backend = 'matplotlib'
StoreOptions.set_options(self.raw, style={'Image':{'cmap':'Blues'}})
Store.current_backend = 'bokeh'
StoreOptions.set_options(self.raw, style={'Image':{'cmap':'Purple'}})
def _create_tmp_path(self):
# Remove this when we use pytest
import tempfile
from pathlib import Path
tmp_dir = tempfile.TemporaryDirectory()
tmp_path = Path(tmp_dir.name)
return tmp_dir, tmp_path
def test_raw_pickle(self):
"""
Test usual pickle saving and loading (no style information preserved)
"""
tmp_dir, tmp_path = self._create_tmp_path() # Pytest remove
fname= tmp_path / 'test_raw_pickle.pkl'
with open(fname,'wb') as handle:
pickle.dump(self.raw, handle)
self.clear_options()
with open(fname,'rb') as handle:
img = pickle.load(handle)
# Data should match
self.assertEqual(self.raw, img)
# But the styles will be lost without using Store.load/Store.dump
pickle.current_backend = 'matplotlib'
mpl_opts = Store.lookup_options('matplotlib', img, 'style').options
self.assertEqual(mpl_opts, {})
# ... across all backends
Store.current_backend = 'bokeh'
bokeh_opts = Store.lookup_options('bokeh', img, 'style').options
self.assertEqual(bokeh_opts, {})
tmp_dir.cleanup() # Pytest remove
def test_pickle_mpl_bokeh(self):
"""
Test pickle saving and loading with Store (style information preserved)
"""
tmp_dir, tmp_path = self._create_tmp_path() # Pytest remove
fname = tmp_path / 'test_pickle_mpl_bokeh.pkl'
with open(fname,'wb') as handle:
Store.dump(self.raw, handle)
self.clear_options()
with open(fname,'rb') as handle:
img = Store.load(handle)
# Data should match
self.assertEqual(self.raw, img)
# Check it is still blue in matplotlib...
Store.current_backend = 'matplotlib'
mpl_opts = Store.lookup_options('matplotlib', img, 'style').options
self.assertEqual(mpl_opts, {'cmap':'Blues'})
# And purple in bokeh..
Store.current_backend = 'bokeh'
bokeh_opts = Store.lookup_options('bokeh', img, 'style').options
self.assertEqual(bokeh_opts, {'cmap':'Purple'})
tmp_dir.cleanup() # Pytest remove