def test_handle_message_ready_reply(self): def assert_ready(msg=None, metadata=None): self.assertEqual(metadata, {'msg_type': "Ready", 'content': ''}) comm = Comm(id='Test') comm.send = assert_ready comm._handle_msg({})
def test_handle_message_ready_reply_with_comm_id(self): def assert_ready(msg=None, metadata=None): self.assertEqual(metadata, {'msg_type': "Ready", 'content': '', 'comm_id': 'Testing id'}) comm = Comm(id='Test') comm.send = assert_ready comm._handle_msg({'comm_id': 'Testing id'})
def test_handle_message_error_reply(self): def raise_error(msg=None, metadata=None): raise Exception('Test') def assert_error(msg=None, metadata=None): self.assertEqual(metadata['msg_type'], "Error") self.assertTrue(metadata['traceback'].endswith('Exception: Test')) comm = Comm(id='Test', on_msg=raise_error) comm.send = assert_error comm._handle_msg({})
def test_handle_message_ready_reply(self): def assert_ready(msg): self.assertEqual(json.loads(msg), { 'msg_type': "Ready", 'content': '' }) comm = Comm(id='Test') comm.send = assert_ready comm._handle_msg({})
def test_handle_message_ready_reply_with_comm_id(self): def assert_ready(msg): decoded = json.loads(msg) self.assertEqual(decoded, { 'msg_type': "Ready", 'content': '', 'comm_id': 'Testing id' }) comm = Comm(id='Test') comm.send = assert_ready comm._handle_msg({'comm_id': 'Testing id'})
def test_handle_message_error_reply(self): def raise_error(msg): raise Exception('Test') def assert_error(msg): decoded = json.loads(msg) self.assertEqual(decoded['msg_type'], "Error") self.assertTrue(decoded['traceback'].endswith('Exception: Test')) comm = Comm(id='Test', on_msg=raise_error) comm.send = assert_error comm._handle_msg({})
def show_embed(panel, max_states=1000, max_opts=3, json=False, save_path='./', load_path=None): """ Renders a static version of a panel in a notebook by evaluating the set of states defined by the widgets in the model. Note this will only work well for simple apps with a relatively small state space. Arguments --------- max_states: int The maximum number of states to embed max_opts: int The maximum number of states for a single widget json: boolean (default=True) Whether to export the data to json files save_path: str (default='./') The path to save json files to load_path: str (default=None) The path or URL the json files will be loaded from. """ from IPython.display import publish_display_data from ..config import config doc = Document() comm = Comm() with config.set(embed=True): model = panel.get_root(doc, comm) embed_state(panel, model, doc, max_states, max_opts, json, save_path, load_path) publish_display_data(*render_model(model))
def test_point_annotator_updates(): annot = PointAnnotator(point_columns=['Size'], points=sample_points) panel = annot.panel() root_model = panel.get_root( comm=Comm()) # Pass comm to ensure update is applied updated_points = dict(sample_points, Size=sample_points['Size'][::-1]) points = Points(updated_points, vdims=['Size'], crs=ccrs.GOOGLE_MERCATOR) annot.points = points fig = root_model.select_one({'type': Plot}) points = fig.renderers[-1] table = root_model.select_one({'type': DataTable}) # Ensure points data matches for k in updated_points: np.testing.assert_allclose(points.data_source.data[k], updated_points[k]) # Ensure point is linked to table point_cbs = points.data_source.js_property_callbacks['change:data'] assert sum([ cb.code == PointTableLinkCallback.source_code for cb in point_cbs ]) == 1 table_cbs = table.source.js_property_callbacks['change:data'] assert len(table_cbs) == 1 assert table_cbs[0].code == PointTableLinkCallback.target_code
def test_update_dynamic_map_with_stream(self): ys = np.arange(10) # Build stream Scale = Stream.define('Scale', scale=1.0) scale_stream = Scale() # Build DynamicMap def build_scatter(scale): return hv.Scatter(ys * scale) dmap = hv.DynamicMap(build_scatter, streams=[scale_stream]) # Create HoloViews Pane using panel so that we can access the plotly pane # used to display the plotly figure dmap_pane = pn.pane.HoloViews(dmap, backend='plotly') # Call get_root to force instantiation of internal plots/models doc = Document() comm = Comm() dmap_pane.get_root(doc, comm) # Get reference to the plotly pane _, plotly_pane = next(iter(dmap_pane._plots.values())) # Check initial data data = plotly_pane.object['data'] self.assertEqual(len(data), 1) self.assertEqual(data[0]['type'], 'scatter') np.testing.assert_equal(data[0]['y'], ys) # Watch object for changes fn = Mock() plotly_pane.param.watch(fn, 'object') # Update stream scale_stream.event(scale=2.0) # Check that figure object was updated data = plotly_pane.object['data'] np.testing.assert_equal(data[0]['y'], ys * 2.0) # Check that object callback was triggered fn.assert_called_once() args, kwargs = fn.call_args_list[0] event = args[0] self.assertIs(event.obj, plotly_pane) self.assertIs(event.new, plotly_pane.object)
def test_poly_annotator_update_models(): annot = PolyAnnotator(poly_columns=['Group'], polys=[sample_poly], vertex_columns=['Weight']) panel = annot.panel() root_model = panel.get_root( comm=Comm()) # Pass comm to ensure update is applied poly = Polygons([dict(sample_poly, Test=1)], vdims=['Test'], crs=ccrs.GOOGLE_MERCATOR) annot.poly_columns = ['Test'] annot.polys = poly fig = root_model.select_one({'type': Plot}) polys = fig.renderers[1] table1, table2 = root_model.select({'type': DataTable}) if 'xs' in table1.source.data: table1, table2 = table2, table1 # Ensure tables are correctly ordered # Ensure poly data matches np.testing.assert_allclose(polys.data_source.data['xs'][0][:-1], sample_poly['Longitude']) np.testing.assert_allclose(polys.data_source.data['ys'][0][:-1], sample_poly['Latitude']) np.testing.assert_allclose(polys.data_source.data['Test'][0], np.ones(6)) # Ensure table and poly data are linked assert table2.source is polys.data_source # Ensure poly is linked to vertex table poly_cbs = polys.data_source.js_property_callbacks['change:data'] assert sum([ cb.code == VertexTableLinkCallback.source_code for cb in poly_cbs ]) == 1 table_cbs = table1.source.js_property_callbacks['change:data'] assert len(table_cbs) == 1 assert table_cbs[0].code == VertexTableLinkCallback.target_code
def test_decode(self): msg = 'Test' self.assertEqual(Comm.decode(msg), msg)
def test_interactive_streams(self): ys = np.arange(10) scatter1 = hv.Scatter(ys) scatter2 = hv.Scatter(ys) scatter3 = hv.Scatter(ys) # Single stream on the first scatter rangexy1 = RangeXY(source=scatter1) # Multiple streams of the same type on second scatter boundsxy2a = BoundsXY(source=scatter2) boundsxy2b = BoundsXY(source=scatter2) # Multiple streams of different types on third scatter rangexy3 = RangeXY(source=scatter3) boundsxy3 = BoundsXY(source=scatter3) selection1d3 = Selection1D(source=scatter3) # Build layout and layout Pane layout = scatter1 + scatter2 + scatter3 layout_pane = pn.pane.HoloViews(layout, backend='plotly') # Get plotly pane reference doc = Document() comm = Comm() layout_pane.get_root(doc, comm) _, plotly_pane = next(iter(layout_pane._plots.values())) # Simulate zoom and check that RangeXY streams updated accordingly plotly_pane.viewport = { 'xaxis.range': [1, 3], 'yaxis.range': [2, 4], 'xaxis2.range': [3, 5], 'yaxis2.range': [4, 6], 'xaxis3.range': [5, 7], 'yaxis3.range': [6, 8], } self.assertEqual(rangexy1.x_range, (1, 3)) self.assertEqual(rangexy1.y_range, (2, 4)) self.assertEqual(rangexy3.x_range, (5, 7)) self.assertEqual(rangexy3.y_range, (6, 8)) plotly_pane.viewport = None self.assertIsNone(rangexy1.x_range) self.assertIsNone(rangexy1.y_range) self.assertIsNone(rangexy3.x_range) self.assertIsNone(rangexy3.y_range) # Simulate box selection and check that BoundsXY and Selection1D streams # update accordingly # Box select on second subplot plotly_pane.selected_data = { 'points': [], 'range': { 'x2': [10, 20], 'y2': [11, 22] } } self.assertEqual(boundsxy2a.bounds, (10, 11, 20, 22)) self.assertEqual(boundsxy2b.bounds, (10, 11, 20, 22)) # Box selecrt on third subplot plotly_pane.selected_data = { 'points': [ {'curveNumber': 2, 'pointNumber': 0}, {'curveNumber': 2, 'pointNumber': 3}, {'curveNumber': 2, 'pointNumber': 7}, ], 'range': { 'x3': [0, 5], 'y3': [1, 6] } } self.assertEqual(boundsxy3.bounds, (0, 1, 5, 6)) self.assertEqual(selection1d3.index, [0, 3, 7]) # bounds streams on scatter 2 are None self.assertIsNone(boundsxy2a.bounds) self.assertIsNone(boundsxy2b.bounds) # Clear selection plotly_pane.selected_data = None self.assertIsNone(boundsxy3.bounds) self.assertIsNone(boundsxy2a.bounds) self.assertIsNone(boundsxy2b.bounds) self.assertEqual(selection1d3.index, [])
def save(panel, filename, title=None, resources=None, template=None, template_variables=None, embed=False, max_states=1000, max_opts=3, embed_json=False, json_prefix='', save_path='./', load_path=None, progress=True): """ Saves Panel objects to file. Arguments --------- panel: Viewable The Panel Viewable to save to file filename: string or file-like object Filename to save the plot to title: string Optional title for the plot resources: bokeh resources One of the valid bokeh.resources (e.g. CDN or INLINE) template: template file, as used by bokeh.file_html. If None will use bokeh defaults template_variables: template_variables file dict, as used by bokeh.file_html embed: bool Whether the state space should be embedded in the saved file. max_states: int The maximum number of states to embed max_opts: int The maximum number of states for a single widget embed_json: boolean (default=True) Whether to export the data to json files json_prefix: str (default='') Prefix for the randomly json directory save_path: str (default='./') The path to save json files to load_path: str (default=None) The path or URL the json files will be loaded from. progress: boolean (default=True) Whether to report progress """ from ..pane import PaneBase if isinstance(panel, PaneBase) and len(panel.layout) > 1: panel = panel.layout as_png = isinstance(filename, string_types) and filename.endswith('png') doc = Document() comm = Comm() with config.set(embed=embed): with swap_html_model(as_png): model = panel.get_root(doc, comm) if embed: embed_state(panel, model, doc, max_states, max_opts, embed_json, json_prefix, save_path, load_path, progress) else: add_to_doc(model, doc, True) if as_png: save_png(model, filename=filename) return elif isinstance(filename, string_types) and not filename.endswith('.html'): filename = filename + '.html' kwargs = {} if title is None: title = 'Panel' if resources is None: resources = CDN if template: kwargs['template'] = template if template_variables: kwargs['template_variables'] = template_variables html = file_html(doc, resources, title, **kwargs) if hasattr(filename, 'write'): html = decode_utf8(html) if isinstance(filename, io.BytesIO): html = html.encode('utf-8') filename.write(html) return with io.open(filename, mode="w", encoding="utf-8") as f: f.write(decode_utf8(html))
def save(panel, filename, title=None, resources=None, template=None, template_variables={}, embed=False, max_states=1000, max_opts=3, embed_json=False, save_path='./', load_path=None): """ Saves Panel objects to file. Arguments --------- panel: Viewable The Panel Viewable to save to file filename: string or file-like object Filename to save the plot to title: string Optional title for the plot resources: bokeh resources One of the valid bokeh.resources (e.g. CDN or INLINE) embed: bool Whether the state space should be embedded in the saved file. max_states: int The maximum number of states to embed max_opts: int The maximum number of states for a single widget embed_json: boolean (default=True) Whether to export the data to json files save_path: str (default='./') The path to save json files to load_path: str (default=None) The path or URL the json files will be loaded from. """ doc = Document() comm = Comm() with config.set(embed=embed): model = panel.get_root(doc, comm) if embed: embed_state(panel, model, doc, max_states, max_opts, embed_json, save_path, load_path) else: add_to_doc(model, doc, True) if isinstance(filename, string_types): if filename.endswith('png'): save_png(model, filename=filename) return if not filename.endswith('.html'): filename = filename + '.html' kwargs = {} if title is None: title = 'Panel' if resources is None: resources = CDN if template: kwargs['template'] = template html = file_html(doc, resources, title, **kwargs) if hasattr(filename, 'write'): filename.write(decode_utf8(html)) return with io.open(filename, mode="w", encoding="utf-8") as f: f.write(decode_utf8(html))
def test_init_comm(self): Comm()
def test_init_comm_id(self): comm = Comm(id='Test') self.assertEqual(comm.id, 'Test')
def save(panel, filename, title=None, resources=None, template=None, template_variables=None, embed=False, max_states=1000, max_opts=3, embed_json=False, json_prefix='', save_path='./', load_path=None, progress=True, embed_states={}, as_png=None, **kwargs): """ Saves Panel objects to file. Arguments --------- panel: Viewable The Panel Viewable to save to file filename: string or file-like object Filename to save the plot to title: string Optional title for the plot resources: bokeh resources One of the valid bokeh.resources (e.g. CDN or INLINE) template: template file, as used by bokeh.file_html. If None will use bokeh defaults template_variables: template_variables file dict, as used by bokeh.file_html embed: bool Whether the state space should be embedded in the saved file. max_states: int The maximum number of states to embed max_opts: int The maximum number of states for a single widget embed_json: boolean (default=True) Whether to export the data to json files json_prefix: str (default='') Prefix for the randomly json directory save_path: str (default='./') The path to save json files to load_path: str (default=None) The path or URL the json files will be loaded from. progress: boolean (default=True) Whether to report progress embed_states: dict (default={}) A dictionary specifying the widget values to embed for each widget save_png: boolean (default=None) To save as a .png. If None save_png will be true if filename is string and ends with png. """ from ..pane import PaneBase from ..template import BaseTemplate if isinstance(panel, PaneBase) and len(panel.layout) > 1: panel = panel.layout if as_png is None: as_png = isinstance(filename, str) and filename.endswith('png') if isinstance(panel, Document): doc = panel else: doc = Document() if resources is None: resources = CDN mode = 'cdn' elif isinstance(resources, str): if resources.lower() == 'cdn': resources = CDN mode = 'cdn' elif resources.lower() == 'inline': resources = INLINE mode = 'inline' else: raise ValueError("Resources %r not recognized, specify one " "of 'CDN' or 'INLINE'." % resources) elif isinstance(resources, BkResources): mode = resources.mode comm = Comm() with config.set(embed=embed): if isinstance(panel, Document): model = panel elif isinstance(panel, BaseTemplate): with set_resource_mode(mode): panel._init_doc(doc, title=title) model = doc else: model = panel.get_root(doc, comm) if embed: embed_state(panel, model, doc, max_states, max_opts, embed_json, json_prefix, save_path, load_path, progress, embed_states) else: add_to_doc(model, doc, True) if as_png: return save_png(model, resources=resources, filename=filename, template=template, template_variables=template_variables, **kwargs) elif isinstance(filename, str) and not filename.endswith('.html'): filename = filename + '.html' kwargs = {} if title is None: title = 'Panel' if template: kwargs['template'] = template if template_variables: kwargs['template_variables'] = template_variables resources = Resources.from_bokeh(resources, absolute=True) # Set resource mode with set_resource_mode(resources): html = file_html(doc, resources, title, **kwargs) if hasattr(filename, 'write'): if isinstance(filename, io.BytesIO): html = html.encode('utf-8') filename.write(html) else: with io.open(filename, mode="w", encoding="utf-8") as f: f.write(html)
def comm(): return Comm()