diff --git a/CHANGELOG.md b/CHANGELOG.md index c6fefd84707..b8d15791e7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,16 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [Unreleased] +## [1.13.0] +### Added +- Python 3.5 has been added as a tested environment for this package. + ### Updated - `plotly.plotly.create_animations` and `plotly.plotly.icreate_animations` now return appropriate error messages if the response is not successful. +### Changed +- The plot-schema from `https://api.plot.ly/plot-schema` is no longer updated on import. + ## [1.12.12] - 2016-12-06 ### Updated - Updated `plotly.min.js` to version 1.20.5 for `plotly.offline`. diff --git a/makefile b/makefile index 8242f8c888e..ca75f11480c 100644 --- a/makefile +++ b/makefile @@ -18,12 +18,15 @@ setup_subs : update_default_schema : @echo "Making sure the default-schema.json file is up to date" - python -c "import json;\ - from plotly.graph_reference import GRAPH_REFERENCE;\ - f = open('plotly/graph_reference/default-schema.json', 'w');\ - json.dump(GRAPH_REFERENCE, f, indent=4, sort_keys=True,\ - separators=(',', ': '));\ + python -c "import requests;\ + from requests.compat import json as _json;\ + response = requests.get('https://api.plot.ly/v2/plot-schema?sha1');\ + f = open('plotly/package_data/default-schema.json', 'w');\ + _json.dump(response.json()['schema'], f, indent=4,\ + sort_keys=True, separators=(',', ': '));\ f.close()" + @echo "Auto-generating graph objects based on updated default-schema." + python update_graph_objs.py install : sync_subs @echo "" @@ -65,7 +68,7 @@ update_plotlyjs_for_offline : cdn_url = 'https://cdn.plot.ly/plotly-latest.min.js';\ response = urllib2.urlopen(cdn_url);\ html = response.read();\ - f = open('./plotly/offline/plotly.min.js', 'w');\ + f = open('./plotly/package_data/plotly.min.js', 'w');\ f.write(html);\ f.close()" @echo "---------------------------------" diff --git a/plotly/files.py b/plotly/files.py index a04523c0254..a97e79fe255 100644 --- a/plotly/files.py +++ b/plotly/files.py @@ -4,7 +4,6 @@ PLOTLY_DIR = os.path.join(os.path.expanduser("~"), ".plotly") CREDENTIALS_FILE = os.path.join(PLOTLY_DIR, ".credentials") CONFIG_FILE = os.path.join(PLOTLY_DIR, ".config") -GRAPH_REFERENCE_FILE = os.path.join(PLOTLY_DIR, ".graph_reference") TEST_DIR = os.path.join(os.path.expanduser("~"), ".test") TEST_FILE = os.path.join(PLOTLY_DIR, ".permission_test") diff --git a/plotly/graph_objs/graph_objs.py b/plotly/graph_objs/graph_objs.py index 65632cadad6..10e6221f09e 100644 --- a/plotly/graph_objs/graph_objs.py +++ b/plotly/graph_objs/graph_objs.py @@ -797,60 +797,333 @@ def create(object_name, *args, **kwargs): return PlotlyDict(*args, **kwargs) -def _add_classes_to_globals(globals): +# AUTO-GENERATED BELOW. DO NOT EDIT! See makefile. + + +class AngularAxis(PlotlyDict): + """ + Valid attributes for 'angularaxis' at path [] under parents (): + + ['domain', 'endpadding', 'range', 'showline', 'showticklabels', + 'tickcolor', 'ticklen', 'tickorientation', 'ticksuffix', 'visible'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'angularaxis' + + +class Annotation(PlotlyDict): + """ + Valid attributes for 'annotation' at path [] under parents (): + + ['align', 'arrowcolor', 'arrowhead', 'arrowsize', 'arrowwidth', 'ax', + 'axref', 'ay', 'ayref', 'bgcolor', 'bordercolor', 'borderpad', + 'borderwidth', 'font', 'opacity', 'ref', 'showarrow', 'text', + 'textangle', 'visible', 'x', 'xanchor', 'xref', 'y', 'yanchor', 'yref'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'annotation' + + +class Annotations(PlotlyList): + """ + Valid items for 'annotations' at path [] under parents (): + ['Annotation'] + + """ + _name = 'annotations' + + +class Area(PlotlyDict): + """ + Valid attributes for 'area' at path [] under parents (): + + ['hoverinfo', 'legendgroup', 'marker', 'name', 'opacity', 'r', 'rsrc', + 'showlegend', 'stream', 't', 'tsrc', 'type', 'uid', 'visible'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'area' + + +class Bar(PlotlyDict): + """ + Valid attributes for 'bar' at path [] under parents (): + + ['bardir', 'base', 'basesrc', 'dx', 'dy', 'error_x', 'error_y', + 'hoverinfo', 'insidetextfont', 'legendgroup', 'marker', 'name', + 'offset', 'offsetsrc', 'opacity', 'orientation', 'outsidetextfont', + 'r', 'rsrc', 'showlegend', 'stream', 't', 'text', 'textfont', + 'textposition', 'textpositionsrc', 'textsrc', 'tsrc', 'type', 'uid', + 'visible', 'width', 'widthsrc', 'x', 'x0', 'xaxis', 'xcalendar', + 'xsrc', 'y', 'y0', 'yaxis', 'ycalendar', 'ysrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'bar' + + +class Box(PlotlyDict): + """ + Valid attributes for 'box' at path [] under parents (): + + ['boxmean', 'boxpoints', 'fillcolor', 'hoverinfo', 'jitter', + 'legendgroup', 'line', 'marker', 'name', 'opacity', 'orientation', + 'pointpos', 'showlegend', 'stream', 'type', 'uid', 'visible', + 'whiskerwidth', 'x', 'x0', 'xaxis', 'xsrc', 'y', 'y0', 'yaxis', 'ysrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'box' + + +class Candlestick(PlotlyDict): + """ + Valid attributes for 'candlestick' at path [] under parents (): + + ['close', 'closesrc', 'decreasing', 'high', 'highsrc', 'hoverinfo', + 'increasing', 'legendgroup', 'line', 'low', 'lowsrc', 'name', + 'opacity', 'open', 'opensrc', 'showlegend', 'stream', 'text', + 'textsrc', 'type', 'uid', 'visible', 'whiskerwidth', 'x', 'xaxis', + 'xcalendar', 'xsrc', 'yaxis'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'candlestick' + + +class Choropleth(PlotlyDict): + """ + Valid attributes for 'choropleth' at path [] under parents (): + + ['autocolorscale', 'colorbar', 'colorscale', 'geo', 'hoverinfo', + 'legendgroup', 'locationmode', 'locations', 'locationssrc', 'marker', + 'name', 'opacity', 'reversescale', 'showlegend', 'showscale', 'stream', + 'text', 'textsrc', 'type', 'uid', 'visible', 'z', 'zauto', 'zmax', + 'zmin', 'zsrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'choropleth' + + +class ColorBar(PlotlyDict): + """ + Valid attributes for 'colorbar' at path [] under parents (): + + ['bgcolor', 'bordercolor', 'borderwidth', 'dtick', 'exponentformat', + 'len', 'lenmode', 'nticks', 'outlinecolor', 'outlinewidth', + 'separatethousands', 'showexponent', 'showticklabels', + 'showtickprefix', 'showticksuffix', 'thickness', 'thicknessmode', + 'tick0', 'tickangle', 'tickcolor', 'tickfont', 'tickformat', 'ticklen', + 'tickmode', 'tickprefix', 'ticks', 'ticksuffix', 'ticktext', + 'ticktextsrc', 'tickvals', 'tickvalssrc', 'tickwidth', 'title', + 'titlefont', 'titleside', 'x', 'xanchor', 'xpad', 'y', 'yanchor', + 'ypad'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + """ - Create and add all the Graph Objects to this module for export. + _name = 'colorbar' + - :param (dict) globals: The globals() dict from this module. +class Contour(PlotlyDict): + """ + Valid attributes for 'contour' at path [] under parents (): + + ['autocolorscale', 'autocontour', 'colorbar', 'colorscale', + 'connectgaps', 'contours', 'dx', 'dy', 'hoverinfo', 'legendgroup', + 'line', 'name', 'ncontours', 'opacity', 'reversescale', 'showlegend', + 'showscale', 'stream', 'text', 'textsrc', 'transpose', 'type', 'uid', + 'visible', 'x', 'x0', 'xaxis', 'xcalendar', 'xsrc', 'xtype', 'y', 'y0', + 'yaxis', 'ycalendar', 'ysrc', 'ytype', 'z', 'zauto', 'zmax', 'zmin', + 'zsrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] """ - for class_name, class_dict in graph_reference.CLASSES.items(): - object_name = class_dict['object_name'] - base_type = class_dict['base_type'] + _name = 'contour' + + +class Contours(PlotlyDict): + """ + Valid attributes for 'contours' at path [] under parents (): + + ['coloring', 'end', 'showlines', 'size', 'start', 'x', 'y', 'z'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'contours' + + +class Data(PlotlyList): + """ + Valid items for 'data' at path [] under parents (): + ['Area', 'Bar', 'Box', 'Candlestick', 'Choropleth', 'Contour', + 'Heatmap', 'Heatmapgl', 'Histogram', 'Histogram2d', + 'Histogram2dcontour', 'Mesh3d', 'Ohlc', 'Pie', 'Pointcloud', 'Scatter', + 'Scatter3d', 'Scattergeo', 'Scattergl', 'Scattermapbox', + 'Scatterternary', 'Surface'] + + """ + _name = 'data' + + def _value_to_graph_object(self, index, value, _raise=True): + + if not isinstance(value, dict): + if _raise: + notes = ['Entry should subclass dict.'] + path = self._get_path() + (index, ) + raise exceptions.PlotlyListEntryError(self, path, + notes=notes) + else: + return + + item = value.get('type', 'scatter') + if item not in graph_reference.ARRAYS['data']['items']: + if _raise: + path = self._get_path() + (0, ) + raise exceptions.PlotlyDataTypeError(self, path) + + return GraphObjectFactory.create(item, _raise=_raise, + _parent=self, + _parent_key=index, **value) + + def get_data(self, flatten=False): + """ + Returns the JSON for the plot with non-data elements stripped. - # This is for backwards compat (e.g., Trace) and future changes. - if object_name is None: - globals[class_name] = base_type - continue + :param (bool) flatten: {'a': {'b': ''}} --> {'a.b': ''} + :returns: (dict|list) Depending on (flat|unflat) + + """ + if flatten: + data = [v.get_data(flatten=flatten) for v in self] + d = {} + taken_names = [] + for i, trace in enumerate(data): + + # we want to give the traces helpful names + # however, we need to be sure they're unique too... + trace_name = trace.pop('name', 'trace_{0}'.format(i)) + if trace_name in taken_names: + j = 1 + new_trace_name = "{0}_{1}".format(trace_name, j) + while new_trace_name in taken_names: + new_trace_name = ( + "{0}_{1}".format(trace_name, j) + ) + j += 1 + trace_name = new_trace_name + taken_names.append(trace_name) - doc = graph_objs_tools.get_help(object_name) - if object_name in graph_reference.ARRAYS: - class_bases = (PlotlyList, ) + # finish up the dot-concatenation + for k, v in trace.items(): + key = "{0}.{1}".format(trace_name, k) + d[key] = v + return d else: - class_bases = (PlotlyDict, ) + return super(Data, self).get_data(flatten=flatten) + + +class ErrorX(PlotlyDict): + """ + Valid attributes for 'error_x' at path [] under parents (): + + ['array', 'arrayminus', 'arrayminussrc', 'arraysrc', 'color', + 'copy_ystyle', 'copy_zstyle', 'opacity', 'symmetric', 'thickness', + 'traceref', 'tracerefminus', 'type', 'value', 'valueminus', 'visible', + 'width'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] - class_dict = {'__doc__': doc, '__name__': class_name, - '_name': object_name} + """ + _name = 'error_x' + + +class ErrorY(PlotlyDict): + """ + Valid attributes for 'error_y' at path [] under parents (): + + ['array', 'arrayminus', 'arrayminussrc', 'arraysrc', 'color', + 'copy_ystyle', 'copy_zstyle', 'opacity', 'symmetric', 'thickness', + 'traceref', 'tracerefminus', 'type', 'value', 'valueminus', 'visible', + 'width'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'error_y' + + +class ErrorZ(PlotlyDict): + """ + Valid attributes for 'error_z' at path [] under parents (): + + ['array', 'arrayminus', 'arrayminussrc', 'arraysrc', 'color', + 'copy_ystyle', 'copy_zstyle', 'opacity', 'symmetric', 'thickness', + 'traceref', 'tracerefminus', 'type', 'value', 'valueminus', 'visible', + 'width'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] - cls = type(str(class_name), class_bases, class_dict) + """ + _name = 'error_z' - globals[class_name] = cls +class Figure(PlotlyDict): + """ + Valid attributes for 'figure' at path [] under parents (): + + ['data', 'layout'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] -def _patch_figure_class(figure_class): + """ + _name = 'figure' def __init__(self, *args, **kwargs): - super(figure_class, self).__init__(*args, **kwargs) + super(Figure, self).__init__(*args, **kwargs) if 'data' not in self: self.data = GraphObjectFactory.create('data', _parent=self, _parent_key='data') - figure_class.__init__ = __init__ # TODO better integrate frames into Figure - #604 def __setitem__(self, key, value, **kwargs): if key == 'frames': super(PlotlyDict, self).__setitem__(key, value) else: - super(figure_class, self).__setitem__(key, value, **kwargs) - figure_class.__setitem__ = __setitem__ + super(Figure, self).__setitem__(key, value, **kwargs) def _get_valid_attributes(self): - super(figure_class, self)._get_valid_attributes() + super(Figure, self)._get_valid_attributes() # TODO better integrate frames into Figure - #604 if 'frames' not in self._valid_attributes: self._valid_attributes.add('frames') return self._valid_attributes - figure_class._get_valid_attributes = _get_valid_attributes def get_data(self, flatten=False): """ @@ -863,19 +1136,18 @@ def get_data(self, flatten=False): """ return self.data.get_data(flatten=flatten) - figure_class.get_data = get_data def to_dataframe(self): """ - Create a pandas dataframe with trace names and keys as column names. + Create a dataframe with trace names and keys as column names. :return: (DataFrame) """ data = self.get_data(flatten=True) from pandas import DataFrame, Series - return DataFrame(dict([(k, Series(v)) for k, v in data.items()])) - figure_class.to_dataframe = to_dataframe + return DataFrame( + dict([(k, Series(v)) for k, v in data.items()])) def print_grid(self): """ @@ -891,14 +1163,14 @@ def print_grid(self): raise Exception("Use plotly.tools.make_subplots " "to create a subplot grid.") print(grid_str) - figure_class.print_grid = print_grid def append_trace(self, trace, row, col): """ - Add a data traces to your figure bound to axes at the row, col index. + Add a trace to your figure bound to axes at the row, col index. The row, col index is generated from figures created with - plotly.tools.make_subplots and can be viewed with Figure.print_grid. + plotly.tools.make_subplots and can be viewed with + Figure.print_grid. :param (dict) trace: The data trace to be bound. :param (int) row: Subplot row index (see Figure.print_grid). @@ -920,7 +1192,8 @@ def append_trace(self, trace, row, col): grid_ref = self._grid_ref except AttributeError: raise Exception("In order to use Figure.append_trace, " - "you must first use plotly.tools.make_subplots " + "you must first use " + "plotly.tools.make_subplots " "to create a subplot grid.") if row <= 0: raise Exception("Row value is out of range. " @@ -931,13 +1204,15 @@ def append_trace(self, trace, row, col): try: ref = grid_ref[row-1][col-1] except IndexError: - raise Exception("The (row, col) pair sent is out of range. " - "Use Figure.print_grid to view the subplot grid. ") + raise Exception("The (row, col) pair sent is out of " + "range. Use Figure.print_grid to view the " + "subplot grid. ") if 'scene' in ref[0]: trace['scene'] = ref[0] if ref[0] not in self['layout']: raise Exception("Something went wrong. " - "The scene object for ({r},{c}) subplot cell " + "The scene object for ({r},{c}) " + "subplot cell " "got deleted.".format(r=row, c=col)) else: xaxis_key = "xaxis{ref}".format(ref=ref[0][1:]) @@ -945,76 +1220,554 @@ def append_trace(self, trace, row, col): if (xaxis_key not in self['layout'] or yaxis_key not in self['layout']): raise Exception("Something went wrong. " - "An axis object for ({r},{c}) subplot cell " - "got deleted.".format(r=row, c=col)) + "An axis object for ({r},{c}) subplot " + "cell got deleted." + .format(r=row, c=col)) trace['xaxis'] = ref[0] trace['yaxis'] = ref[1] self['data'] += [trace] - figure_class.append_trace = append_trace -def _patch_data_class(data_class): +class Font(PlotlyDict): + """ + Valid attributes for 'font' at path [] under parents (): + + ['color', 'family', 'size'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] - def _value_to_graph_object(self, index, value, _raise=True): + """ + _name = 'font' - if not isinstance(value, dict): - if _raise: - notes = ['Entry should subclass dict.'] - path = self._get_path() + (index, ) - raise exceptions.PlotlyListEntryError(self, path, notes=notes) - else: - return - item = value.get('type', 'scatter') - if item not in graph_reference.ARRAYS['data']['items']: - if _raise: - path = self._get_path() + (0, ) - raise exceptions.PlotlyDataTypeError(self, path) +class Frames(dict): + pass - return GraphObjectFactory.create(item, _raise=_raise, _parent=self, - _parent_key=index, **value) - data_class._value_to_graph_object = _value_to_graph_object - def get_data(self, flatten=False): - """ - Returns the JSON for the plot with non-data elements stripped. +class Heatmap(PlotlyDict): + """ + Valid attributes for 'heatmap' at path [] under parents (): + + ['autocolorscale', 'colorbar', 'colorscale', 'connectgaps', 'dx', 'dy', + 'hoverinfo', 'legendgroup', 'name', 'opacity', 'reversescale', + 'showlegend', 'showscale', 'stream', 'text', 'textsrc', 'transpose', + 'type', 'uid', 'visible', 'x', 'x0', 'xaxis', 'xcalendar', 'xgap', + 'xsrc', 'xtype', 'y', 'y0', 'yaxis', 'ycalendar', 'ygap', 'ysrc', + 'ytype', 'z', 'zauto', 'zmax', 'zmin', 'zsmooth', 'zsrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] - :param (bool) flatten: {'a': {'b': ''}} --> {'a.b': ''} - :returns: (dict|list) Depending on (flat|unflat) + """ + _name = 'heatmap' - """ - if flatten: - data = [v.get_data(flatten=flatten) for v in self] - d = {} - taken_names = [] - for i, trace in enumerate(data): - # we want to give the traces helpful names - # however, we need to be sure they're unique too... - trace_name = trace.pop('name', 'trace_{0}'.format(i)) - if trace_name in taken_names: - j = 1 - new_trace_name = "{0}_{1}".format(trace_name, j) - while new_trace_name in taken_names: - new_trace_name = "{0}_{1}".format(trace_name, j) - j += 1 - trace_name = new_trace_name - taken_names.append(trace_name) +class Heatmapgl(PlotlyDict): + """ + Valid attributes for 'heatmapgl' at path [] under parents (): + + ['autocolorscale', 'colorbar', 'colorscale', 'dx', 'dy', 'hoverinfo', + 'legendgroup', 'name', 'opacity', 'reversescale', 'showlegend', + 'showscale', 'stream', 'text', 'textsrc', 'transpose', 'type', 'uid', + 'visible', 'x', 'x0', 'xaxis', 'xsrc', 'xtype', 'y', 'y0', 'yaxis', + 'ysrc', 'ytype', 'z', 'zauto', 'zmax', 'zmin', 'zsrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'heatmapgl' + + +class Histogram(PlotlyDict): + """ + Valid attributes for 'histogram' at path [] under parents (): + + ['autobinx', 'autobiny', 'bardir', 'error_x', 'error_y', 'histfunc', + 'histnorm', 'hoverinfo', 'legendgroup', 'marker', 'name', 'nbinsx', + 'nbinsy', 'opacity', 'orientation', 'showlegend', 'stream', 'text', + 'textsrc', 'type', 'uid', 'visible', 'x', 'xaxis', 'xbins', + 'xcalendar', 'xsrc', 'y', 'yaxis', 'ybins', 'ycalendar', 'ysrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'histogram' + + +class Histogram2d(PlotlyDict): + """ + Valid attributes for 'histogram2d' at path [] under parents (): + + ['autobinx', 'autobiny', 'autocolorscale', 'colorbar', 'colorscale', + 'histfunc', 'histnorm', 'hoverinfo', 'legendgroup', 'marker', 'name', + 'nbinsx', 'nbinsy', 'opacity', 'reversescale', 'showlegend', + 'showscale', 'stream', 'type', 'uid', 'visible', 'x', 'xaxis', 'xbins', + 'xcalendar', 'xgap', 'xsrc', 'y', 'yaxis', 'ybins', 'ycalendar', + 'ygap', 'ysrc', 'z', 'zauto', 'zmax', 'zmin', 'zsmooth', 'zsrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'histogram2d' + + +class Histogram2dContour(PlotlyDict): + """ + Valid attributes for 'histogram2dcontour' at path [] under parents (): + + ['autobinx', 'autobiny', 'autocolorscale', 'autocontour', 'colorbar', + 'colorscale', 'contours', 'histfunc', 'histnorm', 'hoverinfo', + 'legendgroup', 'line', 'marker', 'name', 'nbinsx', 'nbinsy', + 'ncontours', 'opacity', 'reversescale', 'showlegend', 'showscale', + 'stream', 'type', 'uid', 'visible', 'x', 'xaxis', 'xbins', 'xcalendar', + 'xsrc', 'y', 'yaxis', 'ybins', 'ycalendar', 'ysrc', 'z', 'zauto', + 'zmax', 'zmin', 'zsrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'histogram2dcontour' + + +class Histogram2dcontour(PlotlyDict): + """ + Valid attributes for 'histogram2dcontour' at path [] under parents (): + + ['autobinx', 'autobiny', 'autocolorscale', 'autocontour', 'colorbar', + 'colorscale', 'contours', 'histfunc', 'histnorm', 'hoverinfo', + 'legendgroup', 'line', 'marker', 'name', 'nbinsx', 'nbinsy', + 'ncontours', 'opacity', 'reversescale', 'showlegend', 'showscale', + 'stream', 'type', 'uid', 'visible', 'x', 'xaxis', 'xbins', 'xcalendar', + 'xsrc', 'y', 'yaxis', 'ybins', 'ycalendar', 'ysrc', 'z', 'zauto', + 'zmax', 'zmin', 'zsrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'histogram2dcontour' + + +class Layout(PlotlyDict): + """ + Valid attributes for 'layout' at path [] under parents (): + + ['angularaxis', 'annotations', 'autosize', 'bargap', 'bargroupgap', + 'barmode', 'barnorm', 'boxgap', 'boxgroupgap', 'boxmode', 'calendar', + 'direction', 'dragmode', 'font', 'geo', 'height', 'hiddenlabels', + 'hiddenlabelssrc', 'hidesources', 'hovermode', 'images', 'legend', + 'mapbox', 'margin', 'orientation', 'paper_bgcolor', 'plot_bgcolor', + 'radialaxis', 'scene', 'separators', 'shapes', 'showlegend', 'sliders', + 'smith', 'ternary', 'title', 'titlefont', 'updatemenus', 'width', + 'xaxis', 'yaxis'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'layout' + + +class Legend(PlotlyDict): + """ + Valid attributes for 'legend' at path [] under parents (): + + ['bgcolor', 'bordercolor', 'borderwidth', 'font', 'orientation', + 'tracegroupgap', 'traceorder', 'x', 'xanchor', 'y', 'yanchor'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'legend' + + +class Line(PlotlyDict): + """ + Valid attributes for 'line' at path [] under parents (): + + ['autocolorscale', 'cauto', 'cmax', 'cmin', 'color', 'colorscale', + 'colorsrc', 'dash', 'outliercolor', 'outlierwidth', 'reversescale', + 'shape', 'showscale', 'simplify', 'smoothing', 'width', 'widthsrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'line' + + +class Margin(PlotlyDict): + """ + Valid attributes for 'margin' at path [] under parents (): + + ['autoexpand', 'b', 'l', 'pad', 'r', 't'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'margin' + + +class Marker(PlotlyDict): + """ + Valid attributes for 'marker' at path [] under parents (): + + ['autocolorscale', 'blend', 'border', 'cauto', 'cmax', 'cmin', 'color', + 'colorbar', 'colors', 'colorscale', 'colorsrc', 'colorssrc', 'line', + 'maxdisplayed', 'opacity', 'opacitysrc', 'outliercolor', + 'reversescale', 'showscale', 'size', 'sizemax', 'sizemin', 'sizemode', + 'sizeref', 'sizesrc', 'symbol', 'symbolsrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'marker' + + +class Mesh3d(PlotlyDict): + """ + Valid attributes for 'mesh3d' at path [] under parents (): + + ['alphahull', 'color', 'colorbar', 'colorscale', 'contour', + 'delaunayaxis', 'facecolor', 'facecolorsrc', 'flatshading', + 'hoverinfo', 'i', 'intensity', 'intensitysrc', 'isrc', 'j', 'jsrc', + 'k', 'ksrc', 'legendgroup', 'lighting', 'lightposition', 'name', + 'opacity', 'reversescale', 'scene', 'showlegend', 'showscale', + 'stream', 'type', 'uid', 'vertexcolor', 'vertexcolorsrc', 'visible', + 'x', 'xcalendar', 'xsrc', 'y', 'ycalendar', 'ysrc', 'z', 'zcalendar', + 'zsrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'mesh3d' + + +class Ohlc(PlotlyDict): + """ + Valid attributes for 'ohlc' at path [] under parents (): + + ['close', 'closesrc', 'decreasing', 'high', 'highsrc', 'hoverinfo', + 'increasing', 'legendgroup', 'line', 'low', 'lowsrc', 'name', + 'opacity', 'open', 'opensrc', 'showlegend', 'stream', 'text', + 'textsrc', 'tickwidth', 'type', 'uid', 'visible', 'x', 'xaxis', + 'xcalendar', 'xsrc', 'yaxis'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'ohlc' + + +class Pie(PlotlyDict): + """ + Valid attributes for 'pie' at path [] under parents (): + + ['direction', 'dlabel', 'domain', 'hole', 'hoverinfo', + 'insidetextfont', 'label0', 'labels', 'labelssrc', 'legendgroup', + 'marker', 'name', 'opacity', 'outsidetextfont', 'pull', 'pullsrc', + 'rotation', 'scalegroup', 'showlegend', 'sort', 'stream', 'text', + 'textfont', 'textinfo', 'textposition', 'textpositionsrc', 'textsrc', + 'type', 'uid', 'values', 'valuessrc', 'visible'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'pie' + + +class Pointcloud(PlotlyDict): + """ + Valid attributes for 'pointcloud' at path [] under parents (): + + ['hoverinfo', 'indices', 'indicessrc', 'legendgroup', 'marker', 'name', + 'opacity', 'showlegend', 'stream', 'text', 'textsrc', 'type', 'uid', + 'visible', 'x', 'xaxis', 'xbounds', 'xboundssrc', 'xsrc', 'xy', + 'xysrc', 'y', 'yaxis', 'ybounds', 'yboundssrc', 'ysrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'pointcloud' + + +class RadialAxis(PlotlyDict): + """ + Valid attributes for 'radialaxis' at path [] under parents (): + + ['domain', 'endpadding', 'orientation', 'range', 'showline', + 'showticklabels', 'tickcolor', 'ticklen', 'tickorientation', + 'ticksuffix', 'visible'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'radialaxis' + + +class Scatter(PlotlyDict): + """ + Valid attributes for 'scatter' at path [] under parents (): + + ['connectgaps', 'dx', 'dy', 'error_x', 'error_y', 'fill', 'fillcolor', + 'hoverinfo', 'hoveron', 'ids', 'idssrc', 'legendgroup', 'line', + 'marker', 'mode', 'name', 'opacity', 'r', 'rsrc', 'showlegend', + 'stream', 't', 'text', 'textfont', 'textposition', 'textpositionsrc', + 'textsrc', 'tsrc', 'type', 'uid', 'visible', 'x', 'x0', 'xaxis', + 'xcalendar', 'xsrc', 'y', 'y0', 'yaxis', 'ycalendar', 'ysrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'scatter' + + +class Scatter3d(PlotlyDict): + """ + Valid attributes for 'scatter3d' at path [] under parents (): + + ['connectgaps', 'error_x', 'error_y', 'error_z', 'hoverinfo', + 'legendgroup', 'line', 'marker', 'mode', 'name', 'opacity', + 'projection', 'scene', 'showlegend', 'stream', 'surfaceaxis', + 'surfacecolor', 'text', 'textfont', 'textposition', 'textpositionsrc', + 'textsrc', 'type', 'uid', 'visible', 'x', 'xcalendar', 'xsrc', 'y', + 'ycalendar', 'ysrc', 'z', 'zcalendar', 'zsrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'scatter3d' + + +class Scattergeo(PlotlyDict): + """ + Valid attributes for 'scattergeo' at path [] under parents (): + + ['connectgaps', 'fill', 'fillcolor', 'geo', 'hoverinfo', 'lat', + 'latsrc', 'legendgroup', 'line', 'locationmode', 'locations', + 'locationssrc', 'lon', 'lonsrc', 'marker', 'mode', 'name', 'opacity', + 'showlegend', 'stream', 'text', 'textfont', 'textposition', + 'textpositionsrc', 'textsrc', 'type', 'uid', 'visible'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'scattergeo' + + +class Scattergl(PlotlyDict): + """ + Valid attributes for 'scattergl' at path [] under parents (): + + ['connectgaps', 'dx', 'dy', 'error_x', 'error_y', 'fill', 'fillcolor', + 'hoverinfo', 'legendgroup', 'line', 'marker', 'mode', 'name', + 'opacity', 'showlegend', 'stream', 'text', 'textsrc', 'type', 'uid', + 'visible', 'x', 'x0', 'xaxis', 'xcalendar', 'xsrc', 'y', 'y0', 'yaxis', + 'ycalendar', 'ysrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'scattergl' + + +class Scattermapbox(PlotlyDict): + """ + Valid attributes for 'scattermapbox' at path [] under parents (): + + ['connectgaps', 'fill', 'fillcolor', 'hoverinfo', 'lat', 'latsrc', + 'legendgroup', 'line', 'lon', 'lonsrc', 'marker', 'mode', 'name', + 'opacity', 'showlegend', 'stream', 'subplot', 'text', 'textfont', + 'textposition', 'textsrc', 'type', 'uid', 'visible'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'scattermapbox' + + +class Scatterternary(PlotlyDict): + """ + Valid attributes for 'scatterternary' at path [] under parents (): + + ['a', 'asrc', 'b', 'bsrc', 'c', 'connectgaps', 'csrc', 'fill', + 'fillcolor', 'hoverinfo', 'hoveron', 'legendgroup', 'line', 'marker', + 'mode', 'name', 'opacity', 'showlegend', 'stream', 'subplot', 'sum', + 'text', 'textfont', 'textposition', 'textpositionsrc', 'textsrc', + 'type', 'uid', 'visible'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'scatterternary' + + +class Scene(PlotlyDict): + """ + Valid attributes for 'scene' at path [] under parents (): + + ['aspectmode', 'aspectratio', 'bgcolor', 'camera', 'cameraposition', + 'domain', 'dragmode', 'hovermode', 'xaxis', 'yaxis', 'zaxis'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'scene' + + +class Stream(PlotlyDict): + """ + Valid attributes for 'stream' at path [] under parents (): + + ['maxpoints', 'token'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'stream' + + +class Surface(PlotlyDict): + """ + Valid attributes for 'surface' at path [] under parents (): + + ['autocolorscale', 'cauto', 'cmax', 'cmin', 'colorbar', 'colorscale', + 'contours', 'hidesurface', 'hoverinfo', 'legendgroup', 'lighting', + 'lightposition', 'name', 'opacity', 'reversescale', 'scene', + 'showlegend', 'showscale', 'stream', 'surfacecolor', 'surfacecolorsrc', + 'text', 'textsrc', 'type', 'uid', 'visible', 'x', 'xcalendar', 'xsrc', + 'y', 'ycalendar', 'ysrc', 'z', 'zauto', 'zcalendar', 'zmax', 'zmin', + 'zsrc'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'surface' + + +class Trace(dict): + pass + + +class XAxis(PlotlyDict): + """ + Valid attributes for 'xaxis' at path [] under parents (): + + ['anchor', 'autorange', 'autotick', 'backgroundcolor', 'calendar', + 'categoryarray', 'categoryarraysrc', 'categoryorder', 'color', + 'domain', 'dtick', 'exponentformat', 'fixedrange', 'gridcolor', + 'gridwidth', 'hoverformat', 'linecolor', 'linewidth', 'mirror', + 'nticks', 'overlaying', 'position', 'range', 'rangemode', + 'rangeselector', 'rangeslider', 'separatethousands', 'showaxeslabels', + 'showbackground', 'showexponent', 'showgrid', 'showline', 'showspikes', + 'showticklabels', 'showtickprefix', 'showticksuffix', 'side', + 'spikecolor', 'spikesides', 'spikethickness', 'tick0', 'tickangle', + 'tickcolor', 'tickfont', 'tickformat', 'ticklen', 'tickmode', + 'tickprefix', 'ticks', 'ticksuffix', 'ticktext', 'ticktextsrc', + 'tickvals', 'tickvalssrc', 'tickwidth', 'title', 'titlefont', 'type', + 'zeroline', 'zerolinecolor', 'zerolinewidth'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'xaxis' + + +class XBins(PlotlyDict): + """ + Valid attributes for 'xbins' at path [] under parents (): + + ['end', 'size', 'start'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'xbins' - # finish up the dot-concatenation - for k, v in trace.items(): - key = "{0}.{1}".format(trace_name, k) - d[key] = v - return d - else: - return super(data_class, self).get_data(flatten=flatten) - data_class.get_data = get_data +class YAxis(PlotlyDict): + """ + Valid attributes for 'yaxis' at path [] under parents (): + + ['anchor', 'autorange', 'autotick', 'backgroundcolor', 'calendar', + 'categoryarray', 'categoryarraysrc', 'categoryorder', 'color', + 'domain', 'dtick', 'exponentformat', 'fixedrange', 'gridcolor', + 'gridwidth', 'hoverformat', 'linecolor', 'linewidth', 'mirror', + 'nticks', 'overlaying', 'position', 'range', 'rangemode', + 'separatethousands', 'showaxeslabels', 'showbackground', + 'showexponent', 'showgrid', 'showline', 'showspikes', 'showticklabels', + 'showtickprefix', 'showticksuffix', 'side', 'spikecolor', 'spikesides', + 'spikethickness', 'tick0', 'tickangle', 'tickcolor', 'tickfont', + 'tickformat', 'ticklen', 'tickmode', 'tickprefix', 'ticks', + 'ticksuffix', 'ticktext', 'ticktextsrc', 'tickvals', 'tickvalssrc', + 'tickwidth', 'title', 'titlefont', 'type', 'zeroline', 'zerolinecolor', + 'zerolinewidth'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'yaxis' -_add_classes_to_globals(globals()) -_patch_figure_class(globals()['Figure']) -_patch_data_class(globals()['Data']) -# We don't want to expose this module to users, just the classes. -# See http://blog.labix.org/2008/06/27/watch-out-for-listdictkeys-in-python-3 -__all__ = list(graph_reference.CLASSES.keys()) +class YBins(PlotlyDict): + """ + Valid attributes for 'ybins' at path [] under parents (): + + ['end', 'size', 'start'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'ybins' + + +class ZAxis(PlotlyDict): + """ + Valid attributes for 'zaxis' at path [] under parents (): + + ['autorange', 'backgroundcolor', 'calendar', 'categoryarray', + 'categoryarraysrc', 'categoryorder', 'color', 'dtick', + 'exponentformat', 'fixedrange', 'gridcolor', 'gridwidth', + 'hoverformat', 'linecolor', 'linewidth', 'mirror', 'nticks', 'range', + 'rangemode', 'separatethousands', 'showaxeslabels', 'showbackground', + 'showexponent', 'showgrid', 'showline', 'showspikes', 'showticklabels', + 'showtickprefix', 'showticksuffix', 'spikecolor', 'spikesides', + 'spikethickness', 'tick0', 'tickangle', 'tickcolor', 'tickfont', + 'tickformat', 'ticklen', 'tickmode', 'tickprefix', 'ticks', + 'ticksuffix', 'ticktext', 'ticktextsrc', 'tickvals', 'tickvalssrc', + 'tickwidth', 'title', 'titlefont', 'type', 'zeroline', 'zerolinecolor', + 'zerolinewidth'] + + Run `.help('attribute')` on any of the above. + '' is the object at [] + + """ + _name = 'zaxis' + +__all__ = [cls for cls in graph_reference.CLASSES.keys() if cls in globals()] diff --git a/plotly/graph_objs/graph_objs_tools.py b/plotly/graph_objs/graph_objs_tools.py index 334927ef4c4..950adab9455 100644 --- a/plotly/graph_objs/graph_objs_tools.py +++ b/plotly/graph_objs/graph_objs_tools.py @@ -37,7 +37,8 @@ def _list_help(object_name, path=(), parent_object_names=()): items = graph_reference.ARRAYS[object_name]['items'] items_classes = [graph_reference.string_to_class_name(item) for item in items] - lines = textwrap.wrap(repr(items_classes), width=LINE_SIZE-TAB_SIZE) + items_classes.sort() + lines = textwrap.wrap(repr(items_classes), width=LINE_SIZE-TAB_SIZE-1) help_dict = { 'object_name': object_name, @@ -54,9 +55,10 @@ def _list_help(object_name, path=(), parent_object_names=()): def _dict_object_help(object_name, path, parent_object_names): """See get_help().""" - attributes = graph_reference.get_valid_attributes(object_name, - parent_object_names) - lines = textwrap.wrap(repr(list(attributes)), width=LINE_SIZE-TAB_SIZE) + attributes = list( + graph_reference.get_valid_attributes(object_name, parent_object_names)) + attributes.sort() + lines = textwrap.wrap(repr(list(attributes)), width=LINE_SIZE-TAB_SIZE-1) help_dict = { 'object_name': object_name, @@ -167,7 +169,7 @@ def _dict_attribute_help(object_name, path, parent_object_names, attribute): if isinstance(val, list) and attribute == 'showline': val = val[0] - lines = textwrap.wrap(val, width=LINE_SIZE) + lines = textwrap.wrap(val, width=LINE_SIZE-1) help_string += '\n\t\t'.join(lines) else: help_string += '{}'.format(val) diff --git a/plotly/graph_reference.py b/plotly/graph_reference.py index 1e8eccb0213..007824f0f30 100644 --- a/plotly/graph_reference.py +++ b/plotly/graph_reference.py @@ -4,19 +4,14 @@ """ from __future__ import absolute_import -import hashlib -import json import os import re from pkg_resources import resource_string -import requests import six +from requests.compat import json as _json -from plotly import files, utils - -GRAPH_REFERENCE_PATH = '/v2/plot-schema' -GRAPH_REFERENCE_DOWNLOAD_TIMEOUT = 5 # seconds +from plotly import utils # For backwards compat, we keep this list of previously known objects. @@ -66,45 +61,14 @@ def get_graph_reference(): """ - Attempts to load local copy of graph reference or makes GET request if DNE. + Load graph reference JSON (aka plot-schema) :return: (dict) The graph reference. - :raises: (PlotlyError) When graph reference DNE and GET request fails. """ - default_config = files.FILE_CONTENT[files.CONFIG_FILE] - if files.check_file_permissions(): - graph_reference = utils.load_json_dict(files.GRAPH_REFERENCE_FILE) - config = utils.load_json_dict(files.CONFIG_FILE) - - # TODO: https://github.com/plotly/python-api/issues/293 - plotly_api_domain = config.get('plotly_api_domain', - default_config['plotly_api_domain']) - else: - graph_reference = {} - plotly_api_domain = default_config['plotly_api_domain'] - - sha1 = hashlib.sha1(six.b(str(graph_reference))).hexdigest() - - graph_reference_url = '{}{}?sha1={}'.format(plotly_api_domain, - GRAPH_REFERENCE_PATH, sha1) - try: - response = requests.get(graph_reference_url, - timeout=GRAPH_REFERENCE_DOWNLOAD_TIMEOUT) - response.raise_for_status() - except requests.exceptions.RequestException: - if not graph_reference: - path = os.path.join('graph_reference', 'default-schema.json') - s = resource_string('plotly', path).decode('utf-8') - graph_reference = json.loads(s) - else: - if six.PY3: - content = str(response.content, encoding='utf-8') - else: - content = response.content - data = json.loads(content) - if data['modified']: - graph_reference = data['schema'] + path = os.path.join('package_data', 'default-schema.json') + s = resource_string('plotly', path).decode('utf-8') + graph_reference = _json.loads(s) return utils.decode_unicode(graph_reference) diff --git a/plotly/offline/offline.py b/plotly/offline/offline.py index 1031240714a..965b5af09e0 100644 --- a/plotly/offline/offline.py +++ b/plotly/offline/offline.py @@ -45,7 +45,7 @@ def download_plotlyjs(download_url): def get_plotlyjs(): - path = os.path.join('offline', 'plotly.min.js') + path = os.path.join('package_data', 'plotly.min.js') plotlyjs = resource_string('plotly', path).decode('utf-8') return plotlyjs diff --git a/plotly/graph_reference/default-schema.json b/plotly/package_data/default-schema.json similarity index 100% rename from plotly/graph_reference/default-schema.json rename to plotly/package_data/default-schema.json diff --git a/plotly/widgets/graphWidget.js b/plotly/package_data/graphWidget.js similarity index 100% rename from plotly/widgets/graphWidget.js rename to plotly/package_data/graphWidget.js diff --git a/plotly/offline/plotly.min.js b/plotly/package_data/plotly.min.js similarity index 100% rename from plotly/offline/plotly.min.js rename to plotly/package_data/plotly.min.js diff --git a/plotly/tests/test_core/test_graph_reference/test_graph_reference.py b/plotly/tests/test_core/test_graph_reference/test_graph_reference.py index 38ac0b872b7..84fb8720f72 100644 --- a/plotly/tests/test_core/test_graph_reference/test_graph_reference.py +++ b/plotly/tests/test_core/test_graph_reference/test_graph_reference.py @@ -13,47 +13,18 @@ import six from nose.plugins.attrib import attr -from plotly import files, graph_reference as gr, tools, utils +from plotly import files, graph_reference as gr from plotly.graph_reference import string_to_class_name, get_role from plotly.tests.utils import PlotlyTestCase class TestGraphReferenceCaching(PlotlyTestCase): - def set_graph_reference(self, graph_reference): - if files.check_file_permissions(): - utils.save_json_dict(files.GRAPH_REFERENCE_FILE, graph_reference) - - @attr('slow') - def test_get_graph_reference_outdated(self): - - # if the hash of the current graph reference doesn't match the hash of - # the graph reference a Plotly server has, we should update! - - outdated_graph_reference = {'real': 'old'} - self.set_graph_reference(outdated_graph_reference) - graph_reference = gr.get_graph_reference() - self.assertNotEqual(graph_reference, outdated_graph_reference) - - def test_get_graph_reference_bad_request_local_copy(self): - - # if the request fails (mocked by using a bad url here) and a local - # copy of the graph reference exists, we can just use that. - - tools.set_config_file(plotly_api_domain='api.am.not.here.ly') - local_graph_reference = {'real': 'local'} - self.set_graph_reference(local_graph_reference) - graph_reference = gr.get_graph_reference() - self.assertEqual(graph_reference, local_graph_reference) - - def test_get_graph_reference_bad_request_no_copy(self): + def test_get_graph_reference(self): # if we don't have a graph reference we load an outdated default - tools.set_config_file(plotly_api_domain='api.am.not.here.ly') - empty_graph_reference = {} # set it to a false-y value. - self.set_graph_reference(empty_graph_reference) - path = os.path.join('graph_reference', 'default-schema.json') + path = os.path.join('package_data', 'default-schema.json') s = resource_string('plotly', path).decode('utf-8') default_graph_reference = json.loads(s) graph_reference = gr.get_graph_reference() @@ -62,8 +33,7 @@ def test_get_graph_reference_bad_request_no_copy(self): @attr('slow') def test_default_schema_is_up_to_date(self): api_domain = files.FILE_CONTENT[files.CONFIG_FILE]['plotly_api_domain'] - graph_reference_url = '{}{}?sha1'.format(api_domain, - gr.GRAPH_REFERENCE_PATH) + graph_reference_url = '{}{}?sha1'.format(api_domain, '/v2/plot-schema') response = requests.get(graph_reference_url) if six.PY3: content = str(response.content, encoding='utf-8') @@ -71,7 +41,7 @@ def test_default_schema_is_up_to_date(self): content = response.content schema = json.loads(content)['schema'] - path = os.path.join('graph_reference', 'default-schema.json') + path = os.path.join('package_data', 'default-schema.json') s = resource_string('plotly', path).decode('utf-8') default_schema = json.loads(s) diff --git a/plotly/tests/utils.py b/plotly/tests/utils.py index fb837d74bfa..f8b1438e6f1 100644 --- a/plotly/tests/utils.py +++ b/plotly/tests/utils.py @@ -37,8 +37,6 @@ def stash_files(self): if files.check_file_permissions(): self._credentials = utils.load_json_dict(files.CREDENTIALS_FILE) self._config = utils.load_json_dict(files.CONFIG_FILE) - self._graph_reference = \ - utils.load_json_dict(files.GRAPH_REFERENCE_FILE) def restore_files(self): if files.check_file_permissions(): @@ -46,9 +44,6 @@ def restore_files(self): utils.save_json_dict(files.CREDENTIALS_FILE, self._credentials) if self._config is not None: utils.save_json_dict(files.CONFIG_FILE, self._config) - if self._graph_reference is not None: - utils.save_json_dict(files.GRAPH_REFERENCE_FILE, - self._graph_reference) def stash_session(self): self._session = copy.deepcopy(session._session) diff --git a/plotly/tools.py b/plotly/tools.py index 0b78c535996..e43106a940d 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -22,7 +22,7 @@ from plotly import graph_reference from plotly import session from plotly.files import (CONFIG_FILE, CREDENTIALS_FILE, FILE_CONTENT, - GRAPH_REFERENCE_FILE, check_file_permissions) + check_file_permissions) DEFAULT_PLOTLY_COLORS = ['rgb(31, 119, 180)', 'rgb(255, 127, 14)', 'rgb(44, 160, 44)', 'rgb(214, 39, 40)', @@ -146,11 +146,6 @@ def ensure_local_plotly_files(): del contents[key] utils.save_json_dict(fn, contents) - # make a request to get graph reference if DNE. - utils.ensure_file_exists(GRAPH_REFERENCE_FILE) - utils.save_json_dict(GRAPH_REFERENCE_FILE, - graph_reference.GRAPH_REFERENCE) - else: warnings.warn("Looks like you don't have 'read-write' permission to " "your 'home' ('~') directory or to our '~/.plotly' " diff --git a/plotly/version.py b/plotly/version.py index f89b5cce884..84c54b74824 100644 --- a/plotly/version.py +++ b/plotly/version.py @@ -1 +1 @@ -__version__ = '1.12.12' +__version__ = '1.13.0' diff --git a/plotly/widgets/graph_widget.py b/plotly/widgets/graph_widget.py index ea400d95717..a359474a7d0 100644 --- a/plotly/widgets/graph_widget.py +++ b/plotly/widgets/graph_widget.py @@ -21,7 +21,7 @@ # No officially recommended way to do this in any other way # http://mail.scipy.org/pipermail/ipython-dev/2014-April/013835.html js_widget_code = resource_string('plotly', - 'widgets/graphWidget.js').decode('utf-8') + 'package_data/graphWidget.js').decode('utf-8') display(Javascript(js_widget_code)) diff --git a/setup.py b/setup.py index e72f78ae770..9c50bcbf475 100644 --- a/setup.py +++ b/setup.py @@ -24,9 +24,9 @@ def readme(): 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', 'Topic :: Scientific/Engineering :: Visualization', ], license='MIT', @@ -40,6 +40,6 @@ def readme(): 'plotly/matplotlylib', 'plotly/matplotlylib/mplexporter', 'plotly/matplotlylib/mplexporter/renderers'], - package_data={'plotly': ['graph_reference/*.json', 'widgets/*.js', 'offline/*.js']}, + package_data={'plotly': ['package_data/*']}, install_requires=['requests', 'six', 'pytz'], zip_safe=False) diff --git a/update_graph_objs.py b/update_graph_objs.py new file mode 100644 index 00000000000..71882c8d537 --- /dev/null +++ b/update_graph_objs.py @@ -0,0 +1,270 @@ +from plotly.graph_objs import graph_objs_tools +from plotly.graph_reference import ARRAYS, CLASSES + +FLAG = '# AUTO-GENERATED BELOW. DO NOT EDIT! See makefile.' + + +def get_non_generated_file_lines(): + """ + Copy each line up to our special FLAG line and return. + + :raises: (ValueError) If the FLAG isn't found. + :return: (list) The lines we're copying. + """ + + lines_to_copy = [] + flag_found = False + with open('./plotly/graph_objs/graph_objs.py', 'r') as f: + for line_to_copy in f: + if line_to_copy.startswith(FLAG): + flag_found = True + break + lines_to_copy.append(line_to_copy) + if not flag_found: + raise ValueError( + 'Failed to find flag:\n"{}"\nin graph_objs_tools.py.'.format(FLAG) + ) + return lines_to_copy + + +def print_figure_patch(f): + """Print a patch to our Figure object into the given open file.""" + + print( + ''' + def __init__(self, *args, **kwargs): + super(Figure, self).__init__(*args, **kwargs) + if 'data' not in self: + self.data = GraphObjectFactory.create('data', _parent=self, + _parent_key='data') + + # TODO better integrate frames into Figure - #604 + def __setitem__(self, key, value, **kwargs): + if key == 'frames': + super(PlotlyDict, self).__setitem__(key, value) + else: + super(Figure, self).__setitem__(key, value, **kwargs) + + def _get_valid_attributes(self): + super(Figure, self)._get_valid_attributes() + # TODO better integrate frames into Figure - #604 + if 'frames' not in self._valid_attributes: + self._valid_attributes.add('frames') + return self._valid_attributes + + def get_data(self, flatten=False): + """ + Returns the JSON for the plot with non-data elements stripped. + + Flattening may increase the utility of the result. + + :param (bool) flatten: {'a': {'b': ''}} --> {'a.b': ''} + :returns: (dict|list) Depending on (flat|unflat) + + """ + return self.data.get_data(flatten=flatten) + + def to_dataframe(self): + """ + Create a dataframe with trace names and keys as column names. + + :return: (DataFrame) + + """ + data = self.get_data(flatten=True) + from pandas import DataFrame, Series + return DataFrame( + dict([(k, Series(v)) for k, v in data.items()])) + + def print_grid(self): + """ + Print a visual layout of the figure's axes arrangement. + + This is only valid for figures that are created + with plotly.tools.make_subplots. + + """ + try: + grid_str = self.__dict__['_grid_str'] + except AttributeError: + raise Exception("Use plotly.tools.make_subplots " + "to create a subplot grid.") + print(grid_str) + + def append_trace(self, trace, row, col): + """ + Add a trace to your figure bound to axes at the row, col index. + + The row, col index is generated from figures created with + plotly.tools.make_subplots and can be viewed with + Figure.print_grid. + + :param (dict) trace: The data trace to be bound. + :param (int) row: Subplot row index (see Figure.print_grid). + :param (int) col: Subplot column index (see Figure.print_grid). + + Example: + # stack two subplots vertically + fig = tools.make_subplots(rows=2) + + This is the format of your plot grid: + [ (1,1) x1,y1 ] + [ (2,1) x2,y2 ] + + fig.append_trace(Scatter(x=[1,2,3], y=[2,1,2]), 1, 1) + fig.append_trace(Scatter(x=[1,2,3], y=[2,1,2]), 2, 1) + + """ + try: + grid_ref = self._grid_ref + except AttributeError: + raise Exception("In order to use Figure.append_trace, " + "you must first use " + "plotly.tools.make_subplots " + "to create a subplot grid.") + if row <= 0: + raise Exception("Row value is out of range. " + "Note: the starting cell is (1, 1)") + if col <= 0: + raise Exception("Col value is out of range. " + "Note: the starting cell is (1, 1)") + try: + ref = grid_ref[row-1][col-1] + except IndexError: + raise Exception("The (row, col) pair sent is out of " + "range. Use Figure.print_grid to view the " + "subplot grid. ") + if 'scene' in ref[0]: + trace['scene'] = ref[0] + if ref[0] not in self['layout']: + raise Exception("Something went wrong. " + "The scene object for ({r},{c}) " + "subplot cell " + "got deleted.".format(r=row, c=col)) + else: + xaxis_key = "xaxis{ref}".format(ref=ref[0][1:]) + yaxis_key = "yaxis{ref}".format(ref=ref[1][1:]) + if (xaxis_key not in self['layout'] + or yaxis_key not in self['layout']): + raise Exception("Something went wrong. " + "An axis object for ({r},{c}) subplot " + "cell got deleted." + .format(r=row, c=col)) + trace['xaxis'] = ref[0] + trace['yaxis'] = ref[1] + self['data'] += [trace] +''', file=f, end='' + ) + + +def print_data_patch(f): + """Print a patch to our Data object into the given open file.""" + print( + ''' + def _value_to_graph_object(self, index, value, _raise=True): + + if not isinstance(value, dict): + if _raise: + notes = ['Entry should subclass dict.'] + path = self._get_path() + (index, ) + raise exceptions.PlotlyListEntryError(self, path, + notes=notes) + else: + return + + item = value.get('type', 'scatter') + if item not in graph_reference.ARRAYS['data']['items']: + if _raise: + path = self._get_path() + (0, ) + raise exceptions.PlotlyDataTypeError(self, path) + + return GraphObjectFactory.create(item, _raise=_raise, + _parent=self, + _parent_key=index, **value) + + def get_data(self, flatten=False): + """ + Returns the JSON for the plot with non-data elements stripped. + + :param (bool) flatten: {'a': {'b': ''}} --> {'a.b': ''} + :returns: (dict|list) Depending on (flat|unflat) + + """ + if flatten: + data = [v.get_data(flatten=flatten) for v in self] + d = {} + taken_names = [] + for i, trace in enumerate(data): + + # we want to give the traces helpful names + # however, we need to be sure they're unique too... + trace_name = trace.pop('name', 'trace_{0}'.format(i)) + if trace_name in taken_names: + j = 1 + new_trace_name = "{0}_{1}".format(trace_name, j) + while new_trace_name in taken_names: + new_trace_name = ( + "{0}_{1}".format(trace_name, j) + ) + j += 1 + trace_name = new_trace_name + taken_names.append(trace_name) + + # finish up the dot-concatenation + for k, v in trace.items(): + key = "{0}.{1}".format(trace_name, k) + d[key] = v + return d + else: + return super(Data, self).get_data(flatten=flatten) +''', file=f, end='' + ) + + +def print_class(name, f): + class_dict = CLASSES[name] + print('\n', file=f) + object_name = class_dict['object_name'] + base_type = class_dict['base_type'] + + # This is for backwards compat (e.g., Trace) and future changes. + if object_name is None: + print('class {}({}):'.format(name, base_type.__name__), + file=f) + print(' pass', file=f) + return + + doc = graph_objs_tools.get_help(object_name) + if object_name in ARRAYS: + base_name = 'PlotlyList' + else: + base_name = 'PlotlyDict' + print('class {}({}):'.format(name, base_name), file=f) + doc_lines = doc.splitlines() + print(' """', file=f) + for doc_line in doc_lines: + print(' ' + doc_line, file=f) + print('\n """', file=f) + print(" _name = '{}'".format(object_name), file=f) + if name == 'Figure': + print_figure_patch(f) + elif name == 'Data': + print_data_patch(f) + +copied_lines = get_non_generated_file_lines() +with open('./plotly/graph_objs/graph_objs.py', 'w') as graph_objs_file: + + # Keep things *exactly* as they were above our special FLAG. + for line in copied_lines: + print(line, file=graph_objs_file, end='') + print(FLAG, file=graph_objs_file) + + # For each object in the plot schema, generate a class in the file. + class_names = list(CLASSES.keys()) + class_names.sort() + for class_name in class_names: + print_class(class_name, graph_objs_file) + + # Finish off the file by only exporting plot-schema names. + print('\n__all__ = [cls for cls in graph_reference.CLASSES.keys() ' + 'if cls in globals()]', file=graph_objs_file)