diff --git a/CHANGELOG.md b/CHANGELOG.md index 9372139544b..9e86b4514e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added +- Support for rendering plots in [nteract](https://nteract.io/)! + See [https://github.com/nteract/nteract/pull/662](https://github.com/nteract/nteract/pull/662) + for the associated PR in nteract. ### Added - `memoize` decorator added to `plotly.utils` diff --git a/plotly/offline/offline.py b/plotly/offline/offline.py index 2228d52b00d..5a8634b3285 100644 --- a/plotly/offline/offline.py +++ b/plotly/offline/offline.py @@ -273,7 +273,7 @@ def iplot(figure_or_data, show_link=True, link_text='Export to plot.ly', validate=True, image=None, filename='plot_image', image_width=800, image_height=600): """ - Draw plotly graphs inside an IPython notebook without + Draw plotly graphs inside an IPython or Jupyter notebook without connecting to an external server. To save the chart to Plotly Cloud or Plotly Enterprise, use `plotly.plotly.iplot`. @@ -335,7 +335,20 @@ def iplot(figure_or_data, show_link=True, link_text='Export to plot.ly', figure_or_data, config, validate, '100%', 525, True ) - ipython_display.display(ipython_display.HTML(plot_html)) + figure = tools.return_figure_from_figure_or_data(figure_or_data, validate) + + # Though it can add quite a bit to the display-bundle size, we include + # multiple representations of the plot so that the display environment can + # choose which one to act on. + data = _json.loads(_json.dumps(figure['data'], + cls=plotly.utils.PlotlyJSONEncoder)) + layout = _json.loads(_json.dumps(figure.get('layout', {}), + cls=plotly.utils.PlotlyJSONEncoder)) + display_bundle = { + 'application/vnd.plotly.v1+json': {'data': data, 'layout': layout}, + 'text/html': plot_html + } + ipython_display.display(display_bundle, raw=True) if image: if image not in __IMAGE_FORMATS: diff --git a/plotly/tests/test_core/test_file/test_file.py b/plotly/tests/test_core/test_file/test_file.py index c8b3bb8680a..93923140354 100644 --- a/plotly/tests/test_core/test_file/test_file.py +++ b/plotly/tests/test_core/test_file/test_file.py @@ -7,19 +7,19 @@ """ import random import string -import requests -from unittest import TestCase from nose.plugins.attrib import attr import plotly.plotly as py from plotly.exceptions import PlotlyRequestError +from plotly.tests.utils import PlotlyTestCase @attr('slow') -class FolderAPITestCase(TestCase): +class FolderAPITestCase(PlotlyTestCase): def setUp(self): + super(FolderAPITestCase, self).setUp() py.sign_in('PythonTest', '9v9f20pext') def _random_filename(self): diff --git a/plotly/tests/test_core/test_get_figure/test_get_figure.py b/plotly/tests/test_core/test_get_figure/test_get_figure.py index edd2f09c503..257d16769af 100644 --- a/plotly/tests/test_core/test_get_figure/test_get_figure.py +++ b/plotly/tests/test_core/test_get_figure/test_get_figure.py @@ -9,17 +9,12 @@ from unittest import TestCase, skipIf -from nose.plugins.attrib import attr -from nose.tools import raises - import six +from nose.plugins.attrib import attr from plotly import exceptions -from plotly.graph_objs import graph_objs from plotly.plotly import plotly as py - -# username for tests: 'plotlyimagetest' -# api_key for account: '786r5mecv0' +from plotly.tests.utils import PlotlyTestCase def is_trivial(obj): @@ -41,75 +36,59 @@ def is_trivial(obj): return False -@attr('slow') -def test_get_figure(): - un = 'PlotlyImageTest' - ak = '786r5mecv0' - file_id = 2 - py.sign_in(un, ak) - print("getting: https://plot.ly/~{0}/{1}/".format(un, file_id)) - print("###########################################\n\n") - fig = py.get_figure('PlotlyImageTest', str(file_id)) - - -@attr('slow') -def test_get_figure_with_url(): - un = 'PlotlyImageTest' - ak = '786r5mecv0' - url = "https://plot.ly/~PlotlyImageTest/2/" - py.sign_in(un, ak) - print("getting: https://plot.ly/~PlotlyImageTest/2/") - print("###########################################\n\n") - fig = py.get_figure(url) - - -@raises(exceptions.PlotlyError) -def test_get_figure_invalid_1(): - un = 'PlotlyImageTest' - ak = '786r5mecv0' - url = "https://plot.ly/~PlotlyImageTest/a/" - py.sign_in(un, ak) - print("not getting: https://plot.ly/~PlotlyImageTest/a/") - print("###########################################\n\n") - fig = py.get_figure(url) - - -@attr('slow') -@raises(exceptions.PlotlyError) -def test_get_figure_invalid_2(): - un = 'PlotlyImageTest' - ak = '786r5mecv0' - url = "https://plot.ly/~PlotlyImageTest/-1/" - py.sign_in(un, ak) - print("not getting: https://plot.ly/~PlotlyImageTest/-1/") - print("###########################################\n\n") - fig = py.get_figure(url) - - -@attr('slow') -@raises(exceptions.PlotlyError) -def test_get_figure_does_not_exist(): - un = 'PlotlyImageTest' - ak = '786r5mecv0' - url = "https://plot.ly/~PlotlyImageTest/1000000000/" - py.sign_in(un, ak) - print("not getting: https://plot.ly/~PlotlyImageTest/1000000000/") - print("###########################################\n\n") - fig = py.get_figure(url) - - -@attr('slow') -def test_get_figure_raw(): - un = 'PlotlyImageTest' - ak = '786r5mecv0' - file_id = 2 - py.sign_in(un, ak) - print("getting: https://plot.ly/~{0}/{1}/".format(un, file_id)) - print("###########################################\n\n") - fig = py.get_figure('PlotlyImageTest', str(file_id), raw=True) - - -@attr('slow') +class GetFigureTest(PlotlyTestCase): + + @attr('slow') + def test_get_figure(self): + un = 'PlotlyImageTest' + ak = '786r5mecv0' + file_id = 2 + py.sign_in(un, ak) + py.get_figure('PlotlyImageTest', str(file_id)) + + @attr('slow') + def test_get_figure_with_url(self): + un = 'PlotlyImageTest' + ak = '786r5mecv0' + url = "https://plot.ly/~PlotlyImageTest/2/" + py.sign_in(un, ak) + py.get_figure(url) + + def test_get_figure_invalid_1(self): + un = 'PlotlyImageTest' + ak = '786r5mecv0' + url = "https://plot.ly/~PlotlyImageTest/a/" + py.sign_in(un, ak) + with self.assertRaises(exceptions.PlotlyError): + py.get_figure(url) + + @attr('slow') + def test_get_figure_invalid_2(self): + un = 'PlotlyImageTest' + ak = '786r5mecv0' + url = "https://plot.ly/~PlotlyImageTest/-1/" + py.sign_in(un, ak) + with self.assertRaises(exceptions.PlotlyError): + py.get_figure(url) + + @attr('slow') + def test_get_figure_does_not_exist(self): + un = 'PlotlyImageTest' + ak = '786r5mecv0' + url = "https://plot.ly/~PlotlyImageTest/1000000000/" + py.sign_in(un, ak) + with self.assertRaises(exceptions.PlotlyError): + py.get_figure(url) + + @attr('slow') + def test_get_figure_raw(self): + un = 'PlotlyImageTest' + ak = '786r5mecv0' + file_id = 2 + py.sign_in(un, ak) + py.get_figure('PlotlyImageTest', str(file_id), raw=True) + + class TestBytesVStrings(TestCase): @skipIf(not six.PY3, 'Decoding and missing escapes only seen in PY3') @@ -118,6 +97,4 @@ def test_proper_escaping(self): ak = '786r5mecv0' url = "https://plot.ly/~PlotlyImageTest/91/" py.sign_in(un, ak) - print("getting: https://plot.ly/~PlotlyImageTest/91/") - print("###########################################\n\n") - fig = py.get_figure(url) + py.get_figure(url) diff --git a/plotly/tests/test_core/test_get_requests/test_get_requests.py b/plotly/tests/test_core/test_get_requests/test_get_requests.py index 1719d86b38d..6945a3a641e 100644 --- a/plotly/tests/test_core/test_get_requests/test_get_requests.py +++ b/plotly/tests/test_core/test_get_requests/test_get_requests.py @@ -12,6 +12,7 @@ from nose.plugins.attrib import attr from requests.compat import json as _json +from plotly.tests.utils import PlotlyTestCase default_headers = {'plotly-username': '', 'plotly-apikey': '', @@ -20,137 +21,122 @@ server = "https://plot.ly" -# username = "get_test_user" -# password = "password" -# api_key = "vgs6e0cnoi" (currently...) - - -@attr('slow') -def test_user_does_not_exist(): - username = 'user_does_not_exist' - api_key = 'invalid-apikey' - file_owner = 'get_test_user' - file_id = 0 - hd = copy.copy(default_headers) - hd['plotly-username'] = username - hd['plotly-apikey'] = api_key - resource = "/apigetfile/{0}/{1}/".format(file_owner, file_id) - response = requests.get(server + resource, headers=hd) - if six.PY3: - content = _json.loads(response.content.decode('unicode_escape')) - else: - content = _json.loads(response.content) - print(response.status_code) - print(content) - assert response.status_code == 404 - assert (content['error'] == "Aw, snap! We don't have an account for {0}. " - "Want to try again? Sign in is not case " - "sensitive.".format(username)) - - -@attr('slow') -def test_file_does_not_exist(): - username = 'PlotlyImageTest' - api_key = '786r5mecv0' - file_owner = 'get_test_user' - file_id = 1000 - hd = copy.copy(default_headers) - hd['plotly-username'] = username - hd['plotly-apikey'] = api_key - resource = "/apigetfile/{0}/{1}/".format(file_owner, file_id) - response = requests.get(server + resource, headers=hd) - if six.PY3: - content = _json.loads(response.content.decode('unicode_escape')) - else: - content = _json.loads(response.content) - print(response.status_code) - print(content) - assert response.status_code == 404 - assert (content['error'] == "Aw, snap! It looks like this file does not " - "exist. Want to try again?") - - -@attr('slow') -def test_wrong_api_key(): # TODO: does this test the right thing? - username = 'PlotlyImageTest' - api_key = 'invalid-apikey' - file_owner = 'get_test_user' - file_id = 0 - hd = copy.copy(default_headers) - hd['plotly-username'] = username - hd['plotly-apikey'] = api_key - resource = "/apigetfile/{0}/{1}/".format(file_owner, file_id) - response = requests.get(server + resource, headers=hd) - assert response.status_code == 401 - # TODO: check error message? - - -# Locked File -# TODO - -@attr('slow') -def test_private_permission_defined(): - username = 'PlotlyImageTest' - api_key = '786r5mecv0' - file_owner = 'get_test_user' - file_id = 1 # 1 is a private file - hd = copy.copy(default_headers) - hd['plotly-username'] = username - hd['plotly-apikey'] = api_key - resource = "/apigetfile/{0}/{1}/".format(file_owner, file_id) - response = requests.get(server + resource, headers=hd) - if six.PY3: - content = _json.loads(response.content.decode('unicode_escape')) - else: - content = _json.loads(response.content) - print(response.status_code) - print(content) - assert response.status_code == 403 - -# Private File that is shared -# TODO - - -@attr('slow') -def test_missing_headers(): - file_owner = 'get_test_user' - file_id = 0 - resource = "/apigetfile/{0}/{1}/".format(file_owner, file_id) - headers = list(default_headers.keys()) - for header in headers: + +class GetRequestsTest(PlotlyTestCase): + + @attr('slow') + def test_user_does_not_exist(self): + username = 'user_does_not_exist' + api_key = 'invalid-apikey' + file_owner = 'get_test_user' + file_id = 0 + hd = copy.copy(default_headers) + hd['plotly-username'] = username + hd['plotly-apikey'] = api_key + resource = "/apigetfile/{0}/{1}/".format(file_owner, file_id) + response = requests.get(server + resource, headers=hd) + if six.PY3: + content = _json.loads(response.content.decode('unicode_escape')) + else: + content = _json.loads(response.content) + error_message = ("Aw, snap! We don't have an account for {0}. Want to " + "try again? Sign in is not case sensitive." + .format(username)) + self.assertEqual(response.status_code, 404) + self.assertEqual(content['error'], error_message) + + @attr('slow') + def test_file_does_not_exist(self): + username = 'PlotlyImageTest' + api_key = '786r5mecv0' + file_owner = 'get_test_user' + file_id = 1000 + hd = copy.copy(default_headers) + hd['plotly-username'] = username + hd['plotly-apikey'] = api_key + resource = "/apigetfile/{0}/{1}/".format(file_owner, file_id) + response = requests.get(server + resource, headers=hd) + if six.PY3: + content = _json.loads(response.content.decode('unicode_escape')) + else: + content = _json.loads(response.content) + error_message = ("Aw, snap! It looks like this file does " + "not exist. Want to try again?") + self.assertEqual(response.status_code, 404) + self.assertEqual(content['error'], error_message) + + @attr('slow') + def test_wrong_api_key(self): # TODO: does this test the right thing? + username = 'PlotlyImageTest' + api_key = 'invalid-apikey' + file_owner = 'get_test_user' + file_id = 0 + hd = copy.copy(default_headers) + hd['plotly-username'] = username + hd['plotly-apikey'] = api_key + resource = "/apigetfile/{0}/{1}/".format(file_owner, file_id) + response = requests.get(server + resource, headers=hd) + self.assertEqual(response.status_code, 401) + # TODO: check error message? + + # Locked File + # TODO + + @attr('slow') + def test_private_permission_defined(self): + username = 'PlotlyImageTest' + api_key = '786r5mecv0' + file_owner = 'get_test_user' + file_id = 1 # 1 is a private file + hd = copy.copy(default_headers) + hd['plotly-username'] = username + hd['plotly-apikey'] = api_key + resource = "/apigetfile/{0}/{1}/".format(file_owner, file_id) + response = requests.get(server + resource, headers=hd) + if six.PY3: + content = _json.loads(response.content.decode('unicode_escape')) + else: + content = _json.loads(response.content) + self.assertEqual(response.status_code, 403) + + # Private File that is shared + # TODO + + @attr('slow') + def test_missing_headers(self): + file_owner = 'get_test_user' + file_id = 0 + resource = "/apigetfile/{0}/{1}/".format(file_owner, file_id) + headers = list(default_headers.keys()) + for header in headers: + hd = copy.copy(default_headers) + del hd[header] + response = requests.get(server + resource, headers=hd) + if six.PY3: + content = _json.loads(response.content.decode('unicode_escape')) + else: + content = _json.loads(response.content) + self.assertEqual(response.status_code, 422) + + @attr('slow') + def test_valid_request(self): + username = 'PlotlyImageTest' + api_key = '786r5mecv0' + file_owner = 'get_test_user' + file_id = 0 hd = copy.copy(default_headers) - del hd[header] + hd['plotly-username'] = username + hd['plotly-apikey'] = api_key + resource = "/apigetfile/{0}/{1}/".format(file_owner, file_id) response = requests.get(server + resource, headers=hd) if six.PY3: content = _json.loads(response.content.decode('unicode_escape')) else: content = _json.loads(response.content) - print(response.status_code) - print(content) - assert response.status_code == 422 - - -@attr('slow') -def test_valid_request(): - username = 'PlotlyImageTest' - api_key = '786r5mecv0' - file_owner = 'get_test_user' - file_id = 0 - hd = copy.copy(default_headers) - hd['plotly-username'] = username - hd['plotly-apikey'] = api_key - resource = "/apigetfile/{0}/{1}/".format(file_owner, file_id) - response = requests.get(server + resource, headers=hd) - if six.PY3: - content = _json.loads(response.content.decode('unicode_escape')) - else: - content = _json.loads(response.content) - print(response.status_code) - print(content) - assert response.status_code == 200 - # content = _json.loads(res.content) - # response_payload = content['payload'] - # figure = response_payload['figure'] - # if figure['data'][0]['x'] != [u'1', u'2', u'3']: - # print('ERROR') - # return res + self.assertEqual(response.status_code, 200) + # content = _json.loads(res.content) + # response_payload = content['payload'] + # figure = response_payload['figure'] + # if figure['data'][0]['x'] != [u'1', u'2', u'3']: + # print('ERROR') + # return res diff --git a/plotly/tests/test_core/test_grid/test_grid.py b/plotly/tests/test_core/test_grid/test_grid.py index 4ccd3690136..37abd7b81da 100644 --- a/plotly/tests/test_core/test_grid/test_grid.py +++ b/plotly/tests/test_core/test_grid/test_grid.py @@ -9,17 +9,16 @@ import random import string +from unittest import skip -from nose import with_setup from nose.plugins.attrib import attr -from nose.tools import raises -from unittest import skip import plotly.plotly as py from plotly.exceptions import InputError, PlotlyRequestError, PlotlyError from plotly.graph_objs import Scatter from plotly.grid_objs import Column, Grid from plotly.plotly.plotly import parse_grid_id_args +from plotly.tests.utils import PlotlyTestCase def random_filename(): @@ -29,179 +28,154 @@ def random_filename(): return unique_filename -def get_grid(): - c1 = Column([1, 2, 3, 4], 'first column') - c2 = Column(['a', 'b', 'c', 'd'], 'second column') - g = Grid([c1, c2]) - return g - - -def init(): - py.sign_in('PythonTest', '9v9f20pext') - - -def upload_and_return_grid(): - init() - g = get_grid() - unique_filename = random_filename() - - py.grid_ops.upload(g, unique_filename, auto_open=False) - return g - - -# Nominal usage -@attr('slow') -def test_grid_upload(): - upload_and_return_grid() - - -@attr('slow') -@with_setup(init) -def test_grid_upload_in_new_folder(): - g = get_grid() - path = ( - 'new folder: {0}/grid in folder {1}' - .format(random_filename(), random_filename()) - ) - py.grid_ops.upload(g, path, auto_open=False) - - -@attr('slow') -@with_setup(init) -def test_grid_upload_in_existing_folder(): - g = get_grid() - folder = random_filename() - filename = random_filename() - py.file_ops.mkdirs(folder) - path = ( - 'existing folder: {0}/grid in folder {1}' - .format(folder, filename) - ) - py.grid_ops.upload(g, path, auto_open=False) - - -@attr('slow') -def test_column_append(): - g = upload_and_return_grid() - new_col = Column([1, 5, 3], 'new col') - - py.grid_ops.append_columns([new_col], grid=g) - - -@attr('slow') -def test_row_append(): - g = upload_and_return_grid() - new_rows = [[1, 2], [10, 20]] - - py.grid_ops.append_rows(new_rows, grid=g) - - -@attr('slow') -def test_plot_from_grid(): - g = upload_and_return_grid() - url = py.plot([Scatter(xsrc=g[0], ysrc=g[1])], - auto_open=False, filename='plot from grid') - return url, g - +class GridTest(PlotlyTestCase): -@attr('slow') -@with_setup(init) -def test_get_figure_from_references(): - url, g = test_plot_from_grid() - fig = py.get_figure(url) - data = fig['data'] - trace = data[0] - assert(g[0].data == trace['x']) - assert(g[1].data == trace['y']) + # Test grid args + _grid_id = 'chris:3043' + _grid = Grid([]) + _grid.id = _grid_id + _grid_url = 'https://plot.ly/~chris/3043/my-grid' -# Test grid args -_grid_id = 'chris:3043' -_grid = Grid([]) -_grid.id = _grid_id -_grid_url = 'https://plot.ly/~chris/3043/my-grid' + def setUp(self): + super(GridTest, self).setUp() + py.sign_in('PythonTest', '9v9f20pext') + def get_grid(self): + c1 = Column([1, 2, 3, 4], 'first column') + c2 = Column(['a', 'b', 'c', 'd'], 'second column') + g = Grid([c1, c2]) + return g -def test_grid_id_args(): - assert( - parse_grid_id_args(_grid, None) == parse_grid_id_args(None, _grid_url) - ) - - -@raises(InputError) -def test_no_grid_id_args(): - parse_grid_id_args(None, None) - - -@raises(InputError) -def test_overspecified_grid_args(): - parse_grid_id_args(_grid, _grid_url) - - -# Out of order usage -@raises(InputError) -def test_scatter_from_non_uploaded_grid(): - c1 = Column([1, 2, 3, 4], 'first column') - c2 = Column(['a', 'b', 'c', 'd'], 'second column') - g = Grid([c1, c2]) - - Scatter(xsrc=g[0], ysrc=g[1]) - - -@raises(PlotlyError) -def test_column_append_of_non_uploaded_grid(): - c1 = Column([1, 2, 3, 4], 'first column') - c2 = Column(['a', 'b', 'c', 'd'], 'second column') - g = Grid([c1]) - py.grid_ops.append_columns([c2], grid=g) - - -@raises(PlotlyError) -def test_row_append_of_non_uploaded_grid(): - c1 = Column([1, 2, 3, 4], 'first column') - rows = [[1], [2]] - g = Grid([c1]) - py.grid_ops.append_rows(rows, grid=g) - - -# Input Errors -@attr('slow') -@raises(InputError) -def test_unequal_length_rows(): - g = upload_and_return_grid() - rows = [[1, 2], ['to', 'many', 'cells']] - py.grid_ops.append_rows(rows, grid=g) - - -# Test duplicate columns -@raises(InputError) -def test_duplicate_columns(): - c1 = Column([1, 2, 3, 4], 'first column') - c2 = Column(['a', 'b', 'c', 'd'], 'first column') - Grid([c1, c2]) - - -# Test delete -@attr('slow') -@with_setup(init) -def test_delete_grid(): - g = get_grid() - fn = random_filename() - py.grid_ops.upload(g, fn, auto_open=False) - py.grid_ops.delete(g) - py.grid_ops.upload(g, fn, auto_open=False) - - -# Plotly failures -@skip('adding this for now so test_file_tools pass, more info' + - 'https://github.com/plotly/python-api/issues/262') -def test_duplicate_filenames(): - c1 = Column([1, 2, 3, 4], 'first column') - g = Grid([c1]) - - random_chars = [random.choice(string.ascii_uppercase) for _ in range(5)] - unique_filename = 'Valid Grid ' + ''.join(random_chars) - py.grid_ops.upload(g, unique_filename, auto_open=False) - try: + def upload_and_return_grid(self): + g = self.get_grid() + unique_filename = random_filename() + py.grid_ops.upload(g, unique_filename, auto_open=False) + return g + + # Nominal usage + @attr('slow') + def test_grid_upload(self): + self.upload_and_return_grid() + + @attr('slow') + def test_grid_upload_in_new_folder(self): + g = self.get_grid() + path = ( + 'new folder: {0}/grid in folder {1}' + .format(random_filename(), random_filename()) + ) + py.grid_ops.upload(g, path, auto_open=False) + + @attr('slow') + def test_grid_upload_in_existing_folder(self): + g = self.get_grid() + folder = random_filename() + filename = random_filename() + py.file_ops.mkdirs(folder) + path = ( + 'existing folder: {0}/grid in folder {1}' + .format(folder, filename) + ) + py.grid_ops.upload(g, path, auto_open=False) + + @attr('slow') + def test_column_append(self): + g = self.upload_and_return_grid() + new_col = Column([1, 5, 3], 'new col') + py.grid_ops.append_columns([new_col], grid=g) + + @attr('slow') + def test_row_append(self): + g = self.upload_and_return_grid() + new_rows = [[1, 2], [10, 20]] + py.grid_ops.append_rows(new_rows, grid=g) + + @attr('slow') + def test_plot_from_grid(self): + g = self.upload_and_return_grid() + url = py.plot([Scatter(xsrc=g[0], ysrc=g[1])], + auto_open=False, filename='plot from grid') + return url, g + + @attr('slow') + def test_get_figure_from_references(self): + url, g = self.test_plot_from_grid() + fig = py.get_figure(url) + data = fig['data'] + trace = data[0] + assert(g[0].data == trace['x']) + assert(g[1].data == trace['y']) + + def test_grid_id_args(self): + self.assertEqual(parse_grid_id_args(self._grid, None), + parse_grid_id_args(None, self._grid_url)) + + def test_no_grid_id_args(self): + with self.assertRaises(InputError): + parse_grid_id_args(None, None) + + def test_overspecified_grid_args(self): + with self.assertRaises(InputError): + parse_grid_id_args(self._grid, self._grid_url) + + # Out of order usage + def test_scatter_from_non_uploaded_grid(self): + c1 = Column([1, 2, 3, 4], 'first column') + c2 = Column(['a', 'b', 'c', 'd'], 'second column') + g = Grid([c1, c2]) + with self.assertRaises(InputError): + Scatter(xsrc=g[0], ysrc=g[1]) + + def test_column_append_of_non_uploaded_grid(self): + c1 = Column([1, 2, 3, 4], 'first column') + c2 = Column(['a', 'b', 'c', 'd'], 'second column') + g = Grid([c1]) + with self.assertRaises(PlotlyError): + py.grid_ops.append_columns([c2], grid=g) + + def test_row_append_of_non_uploaded_grid(self): + c1 = Column([1, 2, 3, 4], 'first column') + rows = [[1], [2]] + g = Grid([c1]) + with self.assertRaises(PlotlyError): + py.grid_ops.append_rows(rows, grid=g) + + # Input Errors + @attr('slow') + def test_unequal_length_rows(self): + g = self.upload_and_return_grid() + rows = [[1, 2], ['to', 'many', 'cells']] + with self.assertRaises(InputError): + py.grid_ops.append_rows(rows, grid=g) + + # Test duplicate columns + def test_duplicate_columns(self): + c1 = Column([1, 2, 3, 4], 'first column') + c2 = Column(['a', 'b', 'c', 'd'], 'first column') + with self.assertRaises(InputError): + Grid([c1, c2]) + + # Test delete + @attr('slow') + def test_delete_grid(self): + g = self.get_grid() + fn = random_filename() + py.grid_ops.upload(g, fn, auto_open=False) + py.grid_ops.delete(g) + py.grid_ops.upload(g, fn, auto_open=False) + + # Plotly failures + @skip('adding this for now so test_file_tools pass, more info' + + 'https://github.com/plotly/python-api/issues/262') + def test_duplicate_filenames(self): + c1 = Column([1, 2, 3, 4], 'first column') + g = Grid([c1]) + + random_chars = [random.choice(string.ascii_uppercase) + for _ in range(5)] + unique_filename = 'Valid Grid ' + ''.join(random_chars) py.grid_ops.upload(g, unique_filename, auto_open=False) - except PlotlyRequestError as e: - assert(e.status_code == 409) + try: + py.grid_ops.upload(g, unique_filename, auto_open=False) + except PlotlyRequestError as e: + assert(e.status_code == 409) diff --git a/plotly/tests/test_core/test_image/test_image.py b/plotly/tests/test_core/test_image/test_image.py index 409bb8cb48a..7a46fba189e 100644 --- a/plotly/tests/test_core/test_image/test_image.py +++ b/plotly/tests/test_core/test_image/test_image.py @@ -1,6 +1,5 @@ from __future__ import absolute_import -from unittest import TestCase import imghdr import tempfile import os @@ -11,15 +10,15 @@ from plotly import exceptions from plotly.plotly import plotly as py +from plotly.tests.utils import PlotlyTestCase @attr('slow') -class TestImage(TestCase): - def setUp(self): - py.sign_in('PlotlyImageTest', '786r5mecv0', - plotly_domain='https://plot.ly', - plotly_api_domain='https://api.plot.ly') +class TestImage(PlotlyTestCase): + def setUp(self): + super(TestImage, self).setUp() + py.sign_in('PlotlyImageTest', '786r5mecv0') self.data = [{'x': [1, 2, 3], 'y': [3, 1, 6]}] diff --git a/plotly/tests/test_core/test_meta/test_meta.py b/plotly/tests/test_core/test_meta/test_meta.py index f2a3d2b07e8..e2bec3afe17 100644 --- a/plotly/tests/test_core/test_meta/test_meta.py +++ b/plotly/tests/test_core/test_meta/test_meta.py @@ -10,61 +10,54 @@ import random import string -from nose import with_setup from nose.plugins.attrib import attr -from nose.tools import raises from unittest import skip import plotly.plotly as py from plotly.exceptions import PlotlyRequestError from plotly.grid_objs import Column, Grid +from plotly.tests.utils import PlotlyTestCase -def init(): - py.sign_in('PythonTest', '9v9f20pext') +class MetaTest(PlotlyTestCase): + _grid = grid = Grid([Column([1, 2, 3, 4], 'first column')]) + _meta = {"settings": {"scope1": {"model": "Unicorn Finder", "voltage": 4}}} -_grid = grid = Grid([Column([1, 2, 3, 4], 'first column')]) -_meta = {"settings": {"scope1": {"model": "Unicorn Finder", "voltage": 4}}} + def setUp(self): + super(MetaTest, self).setUp() + py.sign_in('PythonTest', '9v9f20pext') + def random_filename(self): + random_chars = [random.choice(string.ascii_uppercase) for _ in range(5)] + unique_filename = 'Valid Grid with Meta '+''.join(random_chars) + return unique_filename -def _random_filename(): - random_chars = [random.choice(string.ascii_uppercase) for _ in range(5)] - unique_filename = 'Valid Grid with Meta '+''.join(random_chars) - return unique_filename + @attr('slow') + def test_upload_meta(self): + unique_filename = self.random_filename() + grid_url = py.grid_ops.upload(self._grid, unique_filename, + auto_open=False) + # Add some Metadata to that grid + py.meta_ops.upload(self._meta, grid_url=grid_url) -@attr('slow') -@with_setup(init) -def test_upload_meta(): - unique_filename = _random_filename() - grid_url = py.grid_ops.upload(_grid, unique_filename, auto_open=False) + @attr('slow') + def test_upload_meta_with_grid(self): + c1 = Column([1, 2, 3, 4], 'first column') + Grid([c1]) - # Add some Metadata to that grid - py.meta_ops.upload(_meta, grid_url=grid_url) + unique_filename = self.random_filename() + py.grid_ops.upload( + self._grid, + unique_filename, + meta=self._meta, + auto_open=False) -@attr('slow') -@with_setup(init) -def test_upload_meta_with_grid(): - c1 = Column([1, 2, 3, 4], 'first column') - Grid([c1]) - - unique_filename = _random_filename() - - py.grid_ops.upload( - _grid, - unique_filename, - meta=_meta, - auto_open=False) - - -@skip('adding this for now so test_file_tools pass, more info' + - 'https://github.com/plotly/python-api/issues/263') -@raises(PlotlyRequestError) -def test_metadata_to_nonexistent_grid(): - init() - - non_exist_meta_url = 'https://local.plot.ly/~GridTest/999999999' - - py.meta_ops.upload(_meta, grid_url=non_exist_meta_url) + @skip('adding this for now so test_file_tools pass, more info' + + 'https://github.com/plotly/python-api/issues/263') + def test_metadata_to_nonexistent_grid(self): + non_exist_meta_url = 'https://local.plot.ly/~GridTest/999999999' + with self.assertRaises(PlotlyRequestError): + py.meta_ops.upload(self._meta, grid_url=non_exist_meta_url) diff --git a/plotly/tests/test_core/test_offline/test_offline.py b/plotly/tests/test_core/test_offline/test_offline.py index cc4b903de71..bf5a51b12e5 100644 --- a/plotly/tests/test_core/test_offline/test_offline.py +++ b/plotly/tests/test_core/test_offline/test_offline.py @@ -4,6 +4,7 @@ """ from __future__ import absolute_import +import os from unittest import TestCase from requests.compat import json as _json @@ -24,7 +25,16 @@ PLOTLYJS = plotly.offline.offline.get_plotlyjs() -class PlotlyOfflineTestCase(TestCase): +class PlotlyOfflineBaseTestCase(TestCase): + def tearDown(self): + # Some offline tests produce an html file. Make sure we clean up :) + try: + os.remove('temp-plot.html') + except OSError: + pass + + +class PlotlyOfflineTestCase(PlotlyOfflineBaseTestCase): def setUp(self): pass @@ -42,7 +52,7 @@ def test_default_plot_generates_expected_html(self): fig['layout'], cls=plotly.utils.PlotlyJSONEncoder) - html = self._read_html(plotly.offline.plot(fig)) + html = self._read_html(plotly.offline.plot(fig, auto_open=False)) # I don't really want to test the entire script output, so # instead just make sure a few of the parts are in here? @@ -54,11 +64,12 @@ def test_default_plot_generates_expected_html(self): self.assertTrue(html.startswith('') and html.endswith('')) def test_including_plotlyjs(self): - html = self._read_html(plotly.offline.plot(fig, include_plotlyjs=False)) + html = self._read_html(plotly.offline.plot(fig, include_plotlyjs=False, + auto_open=False)) self.assertTrue(PLOTLYJS not in html) def test_div_output(self): - html = plotly.offline.plot(fig, output_type='div') + html = plotly.offline.plot(fig, output_type='div', auto_open=False) self.assertTrue('' not in html and '' not in html) self.assertTrue(html.startswith('
') and html.endswith('
')) @@ -69,7 +80,7 @@ def test_autoresizing(self): 'Plotly.Plots.resize(' ] # If width or height wasn't specified, then we add a window resizer - html = self._read_html(plotly.offline.plot(fig)) + html = self._read_html(plotly.offline.plot(fig, auto_open=False)) for resize_code_string in resize_code_strings: self.assertTrue(resize_code_string in html) @@ -79,12 +90,12 @@ def test_autoresizing(self): 'layout': { 'width': 500, 'height': 500 } - }) + }, auto_open=False) for resize_code_string in resize_code_strings: self.assertTrue(resize_code_string not in html) -class PlotlyOfflineOtherDomainTestCase(PlotlyTestCase): +class PlotlyOfflineOtherDomainTestCase(PlotlyOfflineBaseTestCase): def setUp(self): super(PlotlyOfflineOtherDomainTestCase, self).setUp() plotly.tools.set_config_file(plotly_domain='https://stage.plot.ly', diff --git a/plotly/tests/test_core/test_plotly/test_plot.py b/plotly/tests/test_core/test_plotly/test_plot.py index ef7e797cf58..4d5a8eb0b39 100644 --- a/plotly/tests/test_core/test_plotly/test_plot.py +++ b/plotly/tests/test_core/test_plotly/test_plot.py @@ -11,27 +11,23 @@ import six from requests.compat import json as _json -from unittest import TestCase from mock import patch from nose.plugins.attrib import attr -from nose.tools import raises import plotly.tools as tls +from plotly import session from plotly.tests.utils import PlotlyTestCase from plotly.plotly import plotly as py from plotly.exceptions import PlotlyError, PlotlyEmptyDataError from plotly.files import CONFIG_FILE -# username for tests: 'PlotlyImageTest' -# api_key for account: '786r5mecv0' +class TestPlot(PlotlyTestCase): -class TestPlot(TestCase): def setUp(self): - py.sign_in('PlotlyImageTest', '786r5mecv0', - plotly_domain='https://plot.ly') - self.simple_figure = {'data': [{'x': [1, 2, 3], 'y': [2, 1, 2]}]} super(TestPlot, self).setUp() + py.sign_in('PlotlyImageTest', '786r5mecv0') + self.simple_figure = {'data': [{'x': [1, 2, 3], 'y': [2, 1, 2]}]} @attr('slow') def test_plot_valid(self): @@ -50,7 +46,6 @@ def test_plot_valid(self): self.assertEqual(saved_fig['data'][0]['y'], fig['data'][0]['y']) self.assertEqual(saved_fig['layout']['title'], fig['layout']['title']) - @raises(PlotlyError) def test_plot_invalid(self): fig = { 'data': [ @@ -61,15 +56,18 @@ def test_plot_invalid(self): } ] } - py.plot(fig, auto_open=False, filename='plot_invalid') + with self.assertRaises(PlotlyError): + py.plot(fig, auto_open=False, filename='plot_invalid') - @raises(TypeError) def test_plot_invalid_args_1(self): - py.plot(x=[1, 2, 3], y=[2, 1, 2], auto_open=False, filename='plot_invalid') + with self.assertRaises(TypeError): + py.plot(x=[1, 2, 3], y=[2, 1, 2], auto_open=False, + filename='plot_invalid') - @raises(PlotlyError) def test_plot_invalid_args_2(self): - py.plot([1, 2, 3], [2, 1, 2], auto_open=False, filename='plot_invalid') + with self.assertRaises(PlotlyError): + py.plot([1, 2, 3], [2, 1, 2], auto_open=False, + filename='plot_invalid') def test_plot_empty_data(self): self.assertRaises(PlotlyEmptyDataError, py.plot, [], @@ -237,6 +235,12 @@ def setUp(self): self.users_current_mock = patcher.start() self.addCleanup(patcher.stop) + # Some tests specifically check how *file-level* plot options alter + # plot option logic. In order not to re-write that, we simply clear the + # *session* information since it would take precedent. The _session is + # set when you `sign_in`. + session._session['plot_options'].clear() + def test_default_options(self): options = py._plot_option_logic({}) config_options = tls.get_config_file() diff --git a/plotly/tests/test_core/test_session/test_session.py b/plotly/tests/test_core/test_session/test_session.py index 08601c5e6fd..0f5fa0e3d0c 100644 --- a/plotly/tests/test_core/test_session/test_session.py +++ b/plotly/tests/test_core/test_session/test_session.py @@ -2,12 +2,17 @@ from plotly.tests.utils import PlotlyTestCase +from plotly import session from plotly.session import update_session_plot_options, SHARING_OPTIONS from plotly.exceptions import PlotlyError class TestSession(PlotlyTestCase): + def setUp(self): + super(TestSession, self).setUp() + session._session['plot_options'].clear() + def test_update_session_plot_options_invalid_sharing_argument(self): # Return PlotlyError when sharing arguement is not diff --git a/plotly/tests/test_core/test_stream/test_stream.py b/plotly/tests/test_core/test_stream/test_stream.py index 09b3c54a604..190f18a9f39 100644 --- a/plotly/tests/test_core/test_stream/test_stream.py +++ b/plotly/tests/test_core/test_stream/test_stream.py @@ -5,13 +5,13 @@ from __future__ import absolute_import import time -from unittest import TestCase from nose.plugins.attrib import attr import plotly.plotly as py from plotly.graph_objs import (Layout, Scatter, Stream) from plotly import exceptions +from plotly.tests.utils import PlotlyTestCase un = 'PythonAPI' ak = 'ubpiol2cve' @@ -22,9 +22,10 @@ 'plotly_ssl_verification': False} -class TestStreaming(TestCase): +class TestStreaming(PlotlyTestCase): def setUp(self): + super(TestStreaming, self).setUp() py.sign_in(un, ak, **config) @attr('slow') diff --git a/plotly/tests/utils.py b/plotly/tests/utils.py index 2d1113d68b4..267ca5ed216 100644 --- a/plotly/tests/utils.py +++ b/plotly/tests/utils.py @@ -27,6 +27,9 @@ def setUpClass(cls): def setUp(self): self.stash_session() self.stash_files() + defaults = dict(files.FILE_CONTENT[files.CREDENTIALS_FILE], + **files.FILE_CONTENT[files.CONFIG_FILE]) + session.sign_in(**defaults) def tearDown(self): self.restore_files()