diff --git a/circle.yml b/circle.yml index 24515d3de41..540e863960b 100644 --- a/circle.yml +++ b/circle.yml @@ -2,10 +2,9 @@ machine: environment: PLOTLY_PACKAGE_ROOT: /home/ubuntu/python-api PLOTLY_CONFIG_DIR: ${HOME}/.plotly - PLOTLY_PYTHON_VERSIONS: 2.6.8 2.7.8 3.3.3 3.4.1 + PLOTLY_PYTHON_VERSIONS: 2.7.8 3.3.3 3.4.1 PLOTLY_CORE_REQUIREMENTS_FILE: ${PLOTLY_PACKAGE_ROOT}/requirements.txt PLOTLY_OPTIONAL_REQUIREMENTS_FILE: ${PLOTLY_PACKAGE_ROOT}/optional-requirements.txt - PLOTLY_OPTIONAL_REQUIREMENTS_FILE_2_6: ${PLOTLY_PACKAGE_ROOT}/optional-requirements-2-6.txt dependencies: override: diff --git a/circle/setup.sh b/circle/setup.sh index 7d6f06a6852..a0ddc2afb39 100644 --- a/circle/setup.sh +++ b/circle/setup.sh @@ -33,17 +33,8 @@ for version in ${PLOTLY_PYTHON_VERSIONS[@]}; do pip install -r ${PLOTLY_CORE_REQUIREMENTS_FILE} || error_exit "${LINENO}: can't install core reqs for Python ${version}" - # handle funkiness around python 2.6 - if [ ${version:0:3} == '2.6' ] - then - pip install -e '.[PY2.6]' || - error_exit "${LINENO}: can't install extras for Python ${version}" - pip install -r ${PLOTLY_OPTIONAL_REQUIREMENTS_FILE_2_6} || - error_exit "${LINENO}: can't install optional for Python ${version}" - else - pip install -r ${PLOTLY_OPTIONAL_REQUIREMENTS_FILE} || - error_exit "${LINENO}: can't install optional for Python ${version}" - fi + pip install -r ${PLOTLY_OPTIONAL_REQUIREMENTS_FILE} || + error_exit "${LINENO}: can't install optional for Python ${version}" # install some test tools pip install nose coverage || diff --git a/optional-requirements-2-6.txt b/optional-requirements-2-6.txt deleted file mode 100644 index c37ceaeb364..00000000000 --- a/optional-requirements-2-6.txt +++ /dev/null @@ -1,18 +0,0 @@ -### Optional Dependencies for Additional Plotly Functionality ### -### ### -### To install, run: ### -### $ pip install -r optional-requirements.txt ### -### ### -################################################################### - -## numpy (technically, this is covered by matplotlib's deps) ## -numpy - -## matplotlylib dependencies ## -matplotlib==1.3.1 - -## testing dependencies ## -nose==1.3.3 - -## pandas deps for some matplotlib functionality ## -pandas diff --git a/plotly/__init__.py b/plotly/__init__.py index 49961b33da8..36b8131c039 100644 --- a/plotly/__init__.py +++ b/plotly/__init__.py @@ -28,5 +28,5 @@ from __future__ import absolute_import -from plotly import plotly, graph_objs, grid_objs, tools, utils, session +from plotly import plotly, graph_objs, grid_objs, tools, utils, session, offline from plotly.version import __version__ diff --git a/plotly/offline/__init__.py b/plotly/offline/__init__.py new file mode 100644 index 00000000000..df04b62cc01 --- /dev/null +++ b/plotly/offline/__init__.py @@ -0,0 +1,10 @@ +""" +offline +====== +This module provides offline functionality. +""" +from . offline import ( + download_plotlyjs, + init_notebook_mode, + iplot +) diff --git a/plotly/offline/offline.py b/plotly/offline/offline.py new file mode 100644 index 00000000000..cd137d56f0a --- /dev/null +++ b/plotly/offline/offline.py @@ -0,0 +1,189 @@ +""" Plotly Offline + A module to use Plotly's graphing library with Python + without connecting to a public or private plotly enterprise + server. +""" +from __future__ import absolute_import + +import uuid +import json +import os +import requests + +from plotly import utils +from plotly import tools +from plotly.exceptions import PlotlyError +from plotly import session + +PLOTLY_OFFLINE_DIRECTORY = plotlyjs_path = os.path.expanduser( + os.path.join(*'~/.plotly/plotlyjs'.split('/'))) +PLOTLY_OFFLINE_BUNDLE = os.path.join(PLOTLY_OFFLINE_DIRECTORY, + 'plotly-ipython-offline-bundle.js') + + +__PLOTLY_OFFLINE_INITIALIZED = False + + +def download_plotlyjs(download_url): + if not os.path.exists(PLOTLY_OFFLINE_DIRECTORY): + os.makedirs(PLOTLY_OFFLINE_DIRECTORY) + + res = requests.get(download_url) + res.raise_for_status() + + with open(PLOTLY_OFFLINE_BUNDLE, 'wb') as f: + f.write(res.content) + + print('\n'.join([ + 'Success! Now start an IPython notebook and run the following ' + + 'code to make your first offline graph:', + '', + 'import plotly', + 'plotly.offline.init_notebook_mode() ' + '# run at the start of every ipython notebook', + 'plotly.offline.iplot([{"x": [1, 2, 3], "y": [3, 1, 6]}])' + ])) + + +def init_notebook_mode(): + """ + Initialize Plotly Offline mode in an IPython Notebook. + Run this function at the start of an IPython notebook + to load the necessary javascript files for creating + Plotly graphs with plotly.offline.iplot. + """ + if not tools._ipython_imported: + raise ImportError('`iplot` can only run inside an IPython Notebook.') + from IPython.display import HTML, display + + if not os.path.exists(PLOTLY_OFFLINE_BUNDLE): + raise PlotlyError('Plotly Offline source file at {source_path} ' + 'is not found.\n' + 'If you have a Plotly Offline license, then try ' + 'running plotly.offline.download_plotlyjs(url) ' + 'with a licensed download url.\n' + "Don't have a Plotly Offline license? " + 'Contact sales@plot.ly learn more about licensing.\n' + 'Questions? support@plot.ly.' + .format(source_path=PLOTLY_OFFLINE_BUNDLE)) + + global __PLOTLY_OFFLINE_INITIALIZED + __PLOTLY_OFFLINE_INITIALIZED = True + display(HTML('')) + + +def iplot(figure_or_data, show_link=True, link_text='Export to plot.ly'): + """ + Draw plotly graphs inside an IPython notebook without + connecting to an external server. + To save the chart to Plotly Cloud or Plotly Enterprise, use + `plotly.plotly.iplot`. + To embed an image of the chart, use `plotly.image.ishow`. + + figure_or_data -- a plotly.graph_objs.Figure or plotly.graph_objs.Data or + dict or list that describes a Plotly graph. + See https://plot.ly/python/ for examples of + graph descriptions. + + Keyword arguments: + show_link (default=True) -- display a link in the bottom-right corner of + of the chart that will export the chart to + Plotly Cloud or Plotly Enterprise + link_text (default='Export to plot.ly') -- the text of export link + + Example: + ``` + from plotly.offline import init_notebook_mode, iplot + init_notebook_mode() + + iplot([{'x': [1, 2, 3], 'y': [5, 2, 7]}]) + ``` + """ + if not __PLOTLY_OFFLINE_INITIALIZED: + raise PlotlyError('\n'.join([ + 'Plotly Offline mode has not been initialized in this notebook. ' + 'Run: ', + '', + 'import plotly', + 'plotly.offline.init_notebook_mode() ' + '# run at the start of every ipython notebook', + ])) + if not tools._ipython_imported: + raise ImportError('`iplot` can only run inside an IPython Notebook.') + + from IPython.display import HTML, display + if isinstance(figure_or_data, dict): + data = figure_or_data['data'] + layout = figure_or_data.get('layout', {}) + else: + data = figure_or_data + layout = {} + + width = layout.get('width', '100%') + height = layout.get('height', 525) + try: + float(width) + except (ValueError, TypeError): + pass + else: + width = str(width) + 'px' + + try: + float(width) + except (ValueError, TypeError): + pass + else: + width = str(width) + 'px' + + plotdivid = uuid.uuid4() + jdata = json.dumps(data, cls=utils.PlotlyJSONEncoder) + jlayout = json.dumps(layout, cls=utils.PlotlyJSONEncoder) + + if show_link is False: + link_text = '' + + plotly_platform_url = session.get_session_config().get('plotly_domain', + 'https://plot.ly') + if (plotly_platform_url != 'https://plot.ly' and + link_text == 'Export to plot.ly'): + + link_domain = plotly_platform_url\ + .replace('https://', '')\ + .replace('http://', '') + link_text = link_text.replace('plot.ly', link_domain) + + display(HTML( + '' + )) + + script = '\n'.join([ + 'Plotly.plot("{id}", {data}, {layout}).then(function() {{', + ' $(".{id}.loading").remove();', + '}})' + ]).format(id=plotdivid, + data=jdata, + layout=jlayout, + link_text=link_text) + + display(HTML('' + '
' + 'Drawing...
' + '
' + '
' + '' + ''.format(id=plotdivid, script=script, + height=height, width=width))) + + +def plot(): + """ Configured to work with localhost Plotly graph viewer + """ + raise NotImplementedError + diff --git a/plotly/tests/test_core/test_offline/test_offline.py b/plotly/tests/test_core/test_offline/test_offline.py new file mode 100644 index 00000000000..2df7e11b4ad --- /dev/null +++ b/plotly/tests/test_core/test_offline/test_offline.py @@ -0,0 +1,21 @@ +""" +test__offline + +""" +from __future__ import absolute_import + +import os +from unittest import TestCase + +import plotly + + +class PlotlyOfflineTestCase(TestCase): + def test_downloading_file_saves_it_to_the_disk(self): + dummy_js_url = ('https://gist.githubusercontent.com/chriddyp/' + 'f40bd33d1eab6f0715dc/raw/' + '24cd2e4e62ceea79e6e790b3a2c94cda63510ede/test.js') + + plotly.offline.download_plotlyjs(dummy_js_url) + assert (os.path.isfile(plotly.offline.offline.PLOTLY_OFFLINE_BUNDLE) is + True) diff --git a/plotly/tests/test_optional/test_offline/test_offline.py b/plotly/tests/test_optional/test_offline/test_offline.py new file mode 100644 index 00000000000..b7c3d74b304 --- /dev/null +++ b/plotly/tests/test_optional/test_offline/test_offline.py @@ -0,0 +1,41 @@ +""" +test__offline + +""" +from __future__ import absolute_import +from nose.tools import raises +from unittest import TestCase +import os + +from plotly.exceptions import PlotlyError +import plotly + +dummy_js_url = ('https://gist.githubusercontent.com/chriddyp/' + 'f40bd33d1eab6f0715dc/raw/' + '24cd2e4e62ceea79e6e790b3a2c94cda63510ede/' + 'test.js') + + +class PlotlyOfflineTestCase(TestCase): + def _remove_plotlyjs(self): + try: + os.remove(plotly.offline.offline.PLOTLY_OFFLINE_BUNDLE) + except OSError: + pass + + def test_no_errors_are_raised_when_initializing_offline_mode(self): + self._remove_plotlyjs() + plotly.offline.download_plotlyjs(dummy_js_url) + plotly.offline.init_notebook_mode() + plotly.offline.iplot([{'x': [1, 2, 3]}]) + + @raises(PlotlyError) + def test_calling_iplot_before_initializing_raises_an_error(self): + self._remove_plotlyjs() + plotly.offline.download_plotlyjs(dummy_js_url) + plotly.offline.iplot([{'x': [1, 2, 3]}]) + + @raises(PlotlyError) + def test_initializing_before_downloading_raises_an_error(self): + self._remove_plotlyjs() + plotly.offline.init_notebook_mode() diff --git a/plotly/version.py b/plotly/version.py index 3b9b42d14cb..0e1a38d3c2d 100644 --- a/plotly/version.py +++ b/plotly/version.py @@ -1 +1 @@ -__version__ = '1.6.19' +__version__ = '1.7.0' diff --git a/setup.py b/setup.py index dc598596bd7..31fc716da39 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ def readme(): author_email='chris@plot.ly', maintainer='Chris P', maintainer_email='chris@plot.ly', - url='https://plot.ly/api/python', + url='https://plot.ly/python/', description="Python plotting library for collaborative, " "interactive, publication-quality graphs.", long_description=readme(),