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

Version 4 odds and ends #1614

Merged
merged 9 commits into from
Jun 14, 2019
23 changes: 11 additions & 12 deletions packages/python/plotly/_plotly_utils/basevalidators.py
Original file line number Diff line number Diff line change
Expand Up @@ -1033,9 +1033,9 @@ class ColorValidator(BaseValidator):
]
},
"""
re_hex = re.compile('#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})')
re_rgb_etc = re.compile('(rgb|hsl|hsv)a?\([\d.]+%?(,[\d.]+%?){2,3}\)')
re_ddk = re.compile('var\(\-\-.*\)')
re_hex = re.compile(r'#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})')
re_rgb_etc = re.compile(r'(rgb|hsl|hsv)a?\([\d.]+%?(,[\d.]+%?){2,3}\)')
re_ddk = re.compile(r'var\(\-\-.*\)')

named_colors = [
"aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
Expand Down Expand Up @@ -1456,9 +1456,9 @@ def __init__(self, plotly_name,
self.base = dflt
else:
# e.g. regex == '/^y([2-9]|[1-9][0-9]+)?$/'
self.base = re.match('/\^(\w+)', regex).group(1)
self.base = re.match(r'/\^(\w+)', regex).group(1)

self.regex = self.base + "(\d*)"
self.regex = self.base + r"(\d*)"

def description(self):

Expand Down Expand Up @@ -2268,6 +2268,8 @@ def description(self):
that may be specified as:
- A list or tuple of trace instances
(e.g. [Scatter(...), Bar(...)])
- A single trace instance
(e.g. Scatter(...), Bar(...), etc.)
- A list or tuple of dicts of string/value properties where:
- The 'type' property specifies the trace type
{trace_types}
Expand Down Expand Up @@ -2302,7 +2304,10 @@ def validate_coerce(self, v, skip_invalid=False):

if v is None:
v = []
elif isinstance(v, (list, tuple)):
else:
if not isinstance(v, (list, tuple)):
v = [v]

trace_classes = tuple(self.class_map.values())

res = []
Expand Down Expand Up @@ -2355,12 +2360,6 @@ def validate_coerce(self, v, skip_invalid=False):
for trace in v:
trace.uid = str(uuid.uuid4())

else:
if skip_invalid:
v = []
else:
self.raise_invalid_val(v)

return v


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def test_rejection_type(validator):
with pytest.raises(ValueError) as validation_failure:
validator.validate_coerce(val)

assert "Invalid value" in str(validation_failure.value)
assert "Invalid element(s)" in str(validation_failure.value)


def test_rejection_element_type(validator):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import base64

import os
import pytest
from _plotly_utils.basevalidators import ImageUriValidator
import numpy as np

from PIL import Image


Expand All @@ -28,7 +28,9 @@ def test_validator_acceptance(val, validator):
def test_validator_coercion_PIL(validator):
# Single pixel black png (http://png-pixel.com/)

img_path = '_plotly_utils/tests/resources/1x1-black.png'
tests_dir = os.path.dirname(os.path.dirname(__file__))
img_path = os.path.join(tests_dir, 'resources', '1x1-black.png')

with open(img_path, 'rb') as f:
hex_bytes = base64.b64encode(f.read()).decode('ascii')
expected_uri = 'data:image/png;base64,' + hex_bytes
Expand Down
89 changes: 89 additions & 0 deletions packages/python/plotly/plotly/basedatatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,37 @@ def update(self, dict1=None, **kwargs):
self[k] = v
return self

def pop(self, key, *args):
"""
Remove the value associated with the specified key and return it

Parameters
----------
key: str
Property name
dflt
The default value to return if key was not found in figure

Returns
-------
value
The removed value that was previously associated with key

Raises
------
KeyError
If key is not in object and no dflt argument specified
"""
# Handle default
if key not in self and args:
return args[0]
elif key in self:
val = self[key]
self[key] = None
return val
else:
raise KeyError(key)

# Data
# ----
@property
Expand All @@ -520,6 +551,10 @@ def data(self, new_data):
'a list or tuple that contains a permutation of a '
'subset of itself.\n')

# ### Treat None as empty ###
if new_data is None:
new_data = ()

# ### Check valid input type ###
if not isinstance(new_data, (list, tuple)):
err_msg = (err_header + ' Received value with type {typ}'
Expand Down Expand Up @@ -859,6 +894,29 @@ def update_traces(
trace.update(patch, **kwargs)
return self

def update_layout(self, dict1=None, **kwargs):
"""
Update the properties of the figure's layout with a dict and/or with
keyword arguments.

This recursively updates the structure of the original
layout with the values in the input dict / keyword arguments.

Parameters
----------
dict1 : dict
Dictionary of properties to be updated
kwargs :
Keyword/value pair of properties to be updated

Returns
-------
BaseFigure
The Figure object that the update_layout method was called on
"""
self.layout.update(dict1, **kwargs)
return self

def _select_layout_subplots_by_prefix(
self,
prefix,
Expand Down Expand Up @@ -3480,6 +3538,37 @@ def update(self, dict1=None, **kwargs):

return self

def pop(self, key, *args):
"""
Remove the value associated with the specified key and return it

Parameters
----------
key: str
Property name
dflt
The default value to return if key was not found in object

Returns
-------
value
The removed value that was previously associated with key

Raises
------
KeyError
If key is not in object and no dflt argument specified
"""
# Handle default
if key not in self and args:
return args[0]
elif key in self:
val = self[key]
self[key] = None
return val
else:
raise KeyError(key)

@property
def _in_batch_mode(self):
"""
Expand Down
2 changes: 2 additions & 0 deletions packages/python/plotly/plotly/graph_objects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from __future__ import absolute_import
from plotly.graph_objs import *
Original file line number Diff line number Diff line change
Expand Up @@ -111,22 +111,6 @@ def test_append_scatter():
assert d1 == d2


@raises(Exception)
def test_append_scatter_after_deleting_xaxis():
trace = Scatter(x=[1, 2, 3], y=[2, 3, 4])
fig = tls.make_subplots(rows=2, cols=3)
fig['layout'].pop('xaxis5', None)
fig.append_trace(trace, 2, 2)


@raises(Exception)
def test_append_scatter_after_deleting_yaxis():
trace = Scatter(x=[1, 2, 3], y=[2, 3, 4])
fig = tls.make_subplots(rows=2, cols=3)
fig['layout'].pop('yaxis5', None)
fig.append_trace(trace, 2, 2)


def test_append_scatter3d():
expected = Figure(
data=Data([
Expand Down Expand Up @@ -168,13 +152,3 @@ def test_append_scatter3d():

d1, d2 = strip_dict_params(fig['layout'], expected['layout'])
assert d1 == d2


@raises(Exception)
def test_append_scatter3d_after_deleting_scene():
fig = tls.make_subplots(rows=2, cols=1,
specs=[[{'is_3d': True}],
[{'is_3d': True}]])
trace = Scatter3d(x=[1, 2, 3], y=[2, 3, 4], z=[1, 2, 3])
fig['layout'].pop('scene1', None)
fig.append_trace(trace, 1, 1)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import absolute_import

import plotly.graph_objs as go
import plotly.graph_objects as go
from plotly.tests.utils import TestCaseNoTemplate


Expand Down Expand Up @@ -139,3 +139,25 @@ def test_add_trace_underscore_kwarg(self):
fig.add_scatter(y=[2, 1, 3], marker_line_color='green')

self.assertEqual(fig.data[0].marker.line.color, 'green')

def test_scalar_trace_as_data(self):
fig = go.Figure(data=go.Waterfall(y=[2, 1, 3]))
self.assertEqual(fig.data, (go.Waterfall(y=[2, 1, 3]),))

fig = go.Figure(data=dict(type='waterfall', y=[2, 1, 3]))
self.assertEqual(fig.data, (go.Waterfall(y=[2, 1, 3]),))

def test_pop_data(self):
fig = go.Figure(data=go.Waterfall(y=[2, 1, 3]))
self.assertEqual(fig.pop('data'), (go.Waterfall(y=[2, 1, 3]),))
self.assertEqual(fig.data, ())

def test_pop_layout(self):
fig = go.Figure(layout=go.Layout(width=1000))
self.assertEqual(fig.pop('layout'), go.Layout(width=1000))
self.assertEqual(fig.layout, go.Layout())

def test_pop_invalid_key(self):
fig = go.Figure(layout=go.Layout(width=1000))
with self.assertRaises(KeyError):
fig.pop('bogus')
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,40 @@ def test_legacy_title_props_remapped(self):
self.assertIn('titlefont', obj)
self.assertIn('titlefont.family', obj)
self.assertIn('titlefont', iter(obj))


class TestPop(TestCase):
def setUp(self):
self.layout = go.Layout(
width=1000,
title={'text': 'the title', 'font': {'size': 20}},
annotations=[{}, {}],
xaxis2={'range': [1, 2]}
)

def test_pop_valid_simple_prop(self):
self.assertEqual(self.layout.width, 1000)
self.assertEqual(self.layout.pop('width'), 1000)
self.assertIsNone(self.layout.width)

def test_pop_valid_compound_prop(self):
val = self.layout.title
self.assertEqual(self.layout.pop('title'), val)
self.assertEqual(self.layout.title, go.layout.Title())

def test_pop_valid_array_prop(self):
val = self.layout.annotations
self.assertEqual(self.layout.pop('annotations'), val)
self.assertEqual(self.layout.annotations, ())

def test_pop_valid_subplot_prop(self):
val = self.layout.xaxis2
self.assertEqual(self.layout.pop('xaxis2'), val)
self.assertEqual(self.layout.xaxis2, go.layout.XAxis())

def test_pop_invalid_prop_key_error(self):
with self.assertRaises(KeyError):
self.layout.pop('bogus')

def test_pop_invalid_prop_with_default(self):
self.assertEqual(self.layout.pop('bogus', 42), 42)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from unittest import TestCase
import plotly.graph_objects as go


class TestUpdateLayout(TestCase):

def test_update_layout_kwargs(self):
# Create initial figure
fig = go.Figure()
fig.layout.title.font.size = 10

# Grab copy of original figure
orig_fig = go.Figure(fig)

fig.update_layout(title_font_family='Courier New')
orig_fig.layout.update(title_font_family='Courier New')
self.assertEqual(fig, orig_fig)

def test_update_layout_dict(self):
# Create initial figure
fig = go.Figure()
fig.layout.title.font.size = 10

# Grab copy of original figure
orig_fig = go.Figure(fig)

fig.update_layout(dict(title=dict(font=dict(family='Courier New'))))
orig_fig.layout.update(title_font_family='Courier New')
self.assertEqual(fig, orig_fig)
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ def test_invalid_figure_json():
with pytest.raises(ValueError) as err:
pio.to_image(bad_fig, format='png')

assert "Invalid value of type" in str(err.value)
assert "Invalid" in str(err.value)

with pytest.raises(ValueError) as err:
pio.to_image(bad_fig, format='png', validate=False)
Expand Down
9 changes: 9 additions & 0 deletions packages/python/plotly/plotlywidget/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
def _jupyter_nbextension_paths():
return [{
'section': 'notebook',
'src': 'static',
'dest': 'plotlywidget',
'require': 'plotlywidget/extension'
}]

__frontend_version__ = '^0.1'