Skip to content

Commit

Permalink
Test that all elements support empty constructors (#3511)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Feb 21, 2019
1 parent 47d5b10 commit 565358b
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 9 deletions.
2 changes: 2 additions & 0 deletions holoviews/core/data/grid.py
Expand Up @@ -193,6 +193,8 @@ def _infer_interval_breaks(cls, coord, axis=0):
if sys.version_info.major == 2 and len(coord) and isinstance(coord[0], (dt.datetime, dt.date)):
# np.diff does not work on datetimes in python 2
coord = coord.astype('datetime64')
if len(coord) == 0:
return np.array([], dtype=coord.dtype)
deltas = 0.5 * np.diff(coord, axis=axis)
first = np.take(coord, [0], axis=axis) - np.take(deltas, [0], axis=axis)
last = np.take(coord, [-1], axis=axis) + np.take(deltas, [-1], axis=axis)
Expand Down
2 changes: 2 additions & 0 deletions holoviews/element/annotation.py
Expand Up @@ -330,6 +330,8 @@ class Div(Element):
group = param.String(default='Div', constant=True)

def __init__(self, data, **params):
if data is None:
data = ''
if not isinstance(data, basestring):
raise ValueError("Div element html data must be a string "
"type, found %s type." % type(data).__name__)
Expand Down
2 changes: 2 additions & 0 deletions holoviews/element/chart.py
Expand Up @@ -185,6 +185,8 @@ class Histogram(Chart):
_binned = True

def __init__(self, data, edges=None, **params):
if data is None:
data = []
if edges is not None:
self.param.warning(
"Histogram edges should be supplied as a tuple "
Expand Down
12 changes: 11 additions & 1 deletion holoviews/element/graphs.py
Expand Up @@ -716,7 +716,13 @@ def _process(self, element, key=None):
else:
kdims = Nodes.kdims
values, vdims = (), []
nodes = Nodes((mxs, mys, nodes)+values, kdims=kdims, vdims=vdims)

if len(nodes):
node_data = (mxs, mys, nodes)+values
else:
node_data = tuple([] for _ in kdims+vdims)

nodes = Nodes(node_data, kdims=kdims, vdims=vdims)
edges = EdgePaths(paths)
chord = Chord((element.data, nodes, edges), compute=False)
chord._angles = points
Expand All @@ -740,11 +746,15 @@ class Chord(Graph):
group = param.String(default='Chord', constant=True)

def __init__(self, data, kdims=None, vdims=None, compute=True, **params):
if data is None or isinstance(data, list) and data == []:
data = (([], [], []),)

if isinstance(data, tuple):
data = data + (None,)* (3-len(data))
edges, nodes, edgepaths = data
else:
edges, nodes, edgepaths = data, None, None

if nodes is not None:
if not isinstance(nodes, Dataset):
if nodes.ndims == 3:
Expand Down
6 changes: 6 additions & 0 deletions holoviews/element/raster.py
Expand Up @@ -42,6 +42,8 @@ class Raster(Element2D):
The dimension description of the data held in the matrix.""")

def __init__(self, data, kdims=None, vdims=None, extents=None, **params):
if data is None or isinstance(data, list) and data == []:
data = np.zeros((0, 0))
if extents is None:
(d1, d2) = data.shape[:2]
extents = (0, 0, d2, d1)
Expand Down Expand Up @@ -73,6 +75,8 @@ def range(self, dim, data_range=True, dimension_range=True):
idx = self.get_dimension_index(dim)
if data_range and idx == 2:
dimension = self.get_dimension(dim)
if self.data.size == 0:
return np.nan, np.nan
lower, upper = np.nanmin(self.data), np.nanmax(self.data)
if not dimension_range:
return lower, upper
Expand Down Expand Up @@ -835,6 +839,8 @@ class QuadMesh(Dataset, Element2D):
_binned = True

def __init__(self, data, kdims=None, vdims=None, **params):
if data is None or isinstance(data, list) and data == []:
data = ([], [], np.zeros((0, 0)))
super(QuadMesh, self).__init__(data, kdims, vdims, **params)
if not self.interface.gridded:
raise DataError("%s type expects gridded data, %s is columnar."
Expand Down
4 changes: 3 additions & 1 deletion holoviews/element/sankey.py
Expand Up @@ -212,7 +212,7 @@ def initializeNodeBreadth():
nsum = np.sum([node['value'] for node in nodes])
ky = (y1 - y0 - (len(nodes)-1) * py) / nsum
kys.append(ky)
ky = np.min(kys)
ky = np.min(kys) if len(kys) else np.nan

for nodes in node_map.values():
for i, node in enumerate(nodes):
Expand Down Expand Up @@ -309,6 +309,8 @@ class Sankey(Graph):
vdims = param.List(default=[Dimension('Value')])

def __init__(self, data, kdims=None, vdims=None, **params):
if data is None:
data = []
if isinstance(data, tuple):
data = data + (None,)*(3-len(data))
edges, nodes, edgepaths = data
Expand Down
4 changes: 3 additions & 1 deletion holoviews/element/tabular.py
Expand Up @@ -23,7 +23,7 @@ class ItemTable(Element):
ItemTables hold an index Dimension for each value they contain, i.e.
they are equivalent to the keys.""")

vdims = param.List(default=[Dimension('Default')], bounds=(1, None), doc="""
vdims = param.List(default=[Dimension('Default')], bounds=(00, None), doc="""
ItemTables should have only index Dimensions.""")

group = param.String(default="ItemTable", constant=True)
Expand All @@ -40,6 +40,8 @@ def cols(self):


def __init__(self, data, **params):
if data is None:
data = []
if type(data) == dict:
raise ValueError("ItemTable cannot accept a standard Python dictionary "
"as a well-defined item ordering is required.")
Expand Down
5 changes: 4 additions & 1 deletion holoviews/plotting/bokeh/sankey.py
Expand Up @@ -165,7 +165,10 @@ def _compute_labels(self, element, data, mapping):

ys = nodes.dimension_values(1)
nodes = element._sankey['nodes']
offset = (nodes[0]['x1']-nodes[0]['x0'])/4.
if nodes:
offset = (nodes[0]['x1']-nodes[0]['x0'])/4.
else:
offset = 0
if self.label_position == 'right':
xs = np.array([node['x1'] for node in nodes])+offset
else:
Expand Down
34 changes: 29 additions & 5 deletions holoviews/tests/element/testelementconstructors.py
@@ -1,10 +1,12 @@
import param
import numpy as np

from holoviews import (Dimension, Dataset, Curve, Path, Histogram,
HeatMap, Contours, Scatter, Points, Polygons,
VectorField, Spikes, Area, Bars, ErrorBars,
BoxWhisker, Raster, Image, QuadMesh, RGB,
Graph, TriMesh)
from holoviews import (Dimension, Dataset, Element, Annotation, Curve,
Path, Histogram, HeatMap, Contours, Scatter,
Points, Polygons, VectorField, Spikes, Area,
Bars, ErrorBars, BoxWhisker, Raster, Image,
QuadMesh, RGB, Graph, TriMesh, Div)
from holoviews.element.path import BaseShape
from holoviews.element.comparison import ComparisonTestCase

class ElementConstructorTest(ComparisonTestCase):
Expand All @@ -25,6 +27,28 @@ def setUp(self):
self.histogram = Histogram(self.sin, self.hxs)
super(ElementConstructorTest, self).setUp()

def test_empty_element_constructor(self):
failed_elements = []
for name, el in param.concrete_descendents(Element).items():
if issubclass(el, (Annotation, BaseShape, Div)):
continue
try:
el([])
except:
failed_elements.append(name)
self.assertEqual(failed_elements, [])

def test_none_element_constructor(self):
failed_elements = []
for name, el in param.concrete_descendents(Element).items():
if issubclass(el, (Annotation, BaseShape)):
continue
try:
el(None)
except:
failed_elements.append(name)
self.assertEqual(failed_elements, [])

def test_chart_zipconstruct(self):
self.assertEqual(Curve(zip(self.xs, self.sin)), self.curve)

Expand Down

0 comments on commit 565358b

Please sign in to comment.