Skip to content

Commit

Permalink
Add 'cdn' and 'directory' include_plotlyjs options in offline.plot
Browse files Browse the repository at this point in the history
When 'cdn', the resulting html file/div includes a script tag reference
to the plotlyjs cdn.

When 'directory', the resulting html file/div includes a script tag
reference to a plotly.min.js bundle in the same directory as the html
file. If output_type is 'file' then this plotly.min.js bundle is
created in the output directory if it doesn't already exist.
  • Loading branch information
jonmmease committed Oct 22, 2018
1 parent 1494538 commit 0cbcce7
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 29 deletions.
84 changes: 60 additions & 24 deletions plotly/offline/offline.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import time
import webbrowser

import six
from requests.compat import json as _json

import plotly
Expand Down Expand Up @@ -448,10 +449,37 @@ def plot(figure_or_data, show_link=True, link_text='Export to plot.ly',
in a standalone HTML file.
Use 'div' if you are embedding these graphs in an HTML file with
other graphs or HTML markup, like a HTML report or an website.
include_plotlyjs (default=True) -- If True, include the plotly.js
source code in the output file or string.
Set as False if your HTML file already contains a copy of the plotly.js
include_plotlyjs (True | False | 'cdn' | 'directory' - default=True) --
Specifies how the plotly.js library is included in the output html
file or div string.
If True, a script tag containing the plotly.js source code (~3MB)
is included in the output. HTML files generated with this option are
fully self-contained and can be used offline.
If 'cdn', a script tag that references the plotly.js CDN is included
in the output. HTML files generated with this option are about 3MB
smaller than those generated with include_plotlyjs=True, but they
require an active internet connection in order to load the plotly.js
library.
If 'directory', a script tag is included that references an external
plotly.min.js bundle that is assumed to reside in the same
directory as the HTML file. If output_type='file' then the
plotly.min.js bundle is copied into the directory of the resulting
HTML file. If a file named plotly.min.js already exists in the output
directory then this file is left unmodified and no copy is performed.
HTML files generated with this option can be used offline, but they
require a copy of the plotly.min.js bundle in the same directory.
This option is useful when many figures will be saved as HTML files in
the same directory because the plotly.js source code will be included
only once per output directory, rather than once per output file.
If False, no script tag referencing plotly.js is included. This is
useful when output_type='div' and the resulting div string will be
placed inside an HTML document that already loads plotly.js. This
option is not advised when output_type='file' as it will result in
a non-functional html file.
filename (default='temp-plot.html') -- The local filename to save the
outputted chart to. If the filename already exists, it will be
overwritten. This argument only applies if `output_type` is 'file'.
Expand Down Expand Up @@ -494,17 +522,25 @@ def plot(figure_or_data, show_link=True, link_text='Export to plot.ly',
if width == '100%' or height == '100%':
resize_script = _build_resize_script(plotdivid)

if isinstance(include_plotlyjs, six.string_types):
include_plotlyjs = include_plotlyjs.lower()

if include_plotlyjs == 'cdn':
plotly_js_script = """\
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>"""
elif include_plotlyjs == 'directory':
plotly_js_script = '<script src="plotly.min.js"></script>'
elif include_plotlyjs:
plotly_js_script = ''.join([
'<script type="text/javascript">',
get_plotlyjs(),
'</script>',
])
else:
plotly_js_script = ''

if output_type == 'file':
with open(filename, 'w') as f:
if include_plotlyjs:
plotly_js_script = ''.join([
'<script type="text/javascript">',
get_plotlyjs(),
'</script>',
])
else:
plotly_js_script = ''

if image:
if image not in __IMAGE_FORMATS:
raise ValueError('The image parameter must be one of the '
Expand Down Expand Up @@ -532,26 +568,26 @@ def plot(figure_or_data, show_link=True, link_text='Export to plot.ly',
'</body>',
'</html>']))

# Check if we should copy plotly.min.js to output directory
if include_plotlyjs == 'directory':
bundle_path = os.path.join(
os.path.dirname(filename), 'plotly.min.js')

if not os.path.exists(bundle_path):
with open(bundle_path, 'w') as f:
f.write(get_plotlyjs())

url = 'file://' + os.path.abspath(filename)
if auto_open:
webbrowser.open(url)

return url

elif output_type == 'div':
if include_plotlyjs:
return ''.join([
'<div>',
'<script type="text/javascript">',
get_plotlyjs(),
'</script>',
plot_html,
resize_script,
'</div>',
])
else:
return ''.join([

return ''.join([
'<div>',
plotly_js_script,
plot_html,
resize_script,
'</div>',
Expand Down
117 changes: 112 additions & 5 deletions plotly/tests/test_core/test_offline/test_offline.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,20 @@

PLOTLYJS = plotly.offline.offline.get_plotlyjs()

cdn_script = ('<script src="https://cdn.plot.ly/plotly-latest.min.js">'
'</script>')

directory_script = '<script src="plotly.min.js"></script>'


class PlotlyOfflineBaseTestCase(TestCase):
def tearDown(self):
# Some offline tests produce an html file. Make sure we clean up :)
try:
os.remove('temp-plot.html')
# Some tests that produce temp-plot.html]
# also produce plotly.min.js
os.remove('plotly.min.js')
except OSError:
pass

Expand Down Expand Up @@ -71,10 +79,109 @@ def test_default_plot_generates_expected_html(self):
# and it's an <html> doc
self.assertTrue(html.startswith('<html>') and html.endswith('</html>'))

def test_including_plotlyjs(self):
html = self._read_html(plotly.offline.plot(fig, include_plotlyjs=False,
auto_open=False))
self.assertNotIn(PLOTLYJS, html)
def test_including_plotlyjs_truthy_html(self):
# For backwards compatibility all truthy values that aren't otherwise
# recognized are considered true
for include_plotlyjs in [True, 34, 'non-empty-str']:
html = self._read_html(plotly.offline.plot(
fig,
include_plotlyjs=include_plotlyjs,
output_type='file',
auto_open=False))
self.assertIn(PLOTLYJS, html)
self.assertNotIn(cdn_script, html)
self.assertNotIn(directory_script, html)

def test_including_plotlyjs_truthy_div(self):
# For backwards compatibility all truthy values that aren't otherwise
# recognized are considered true
for include_plotlyjs in [True, 34, 'non-empty-str']:
html = plotly.offline.plot(
fig,
include_plotlyjs=include_plotlyjs,
output_type='div')
self.assertIn(PLOTLYJS, html)
self.assertNotIn(cdn_script, html)
self.assertNotIn(directory_script, html)

def test_including_plotlyjs_false_html(self):
# For backwards compatibility all truthy values that aren't otherwise
# recognized are considered true
for include_plotlyjs in [False, 0, '']:
html = self._read_html(plotly.offline.plot(
fig,
include_plotlyjs=include_plotlyjs,
output_type='file',
auto_open=False))
self.assertNotIn(PLOTLYJS, html)
self.assertNotIn(cdn_script, html)
self.assertNotIn(directory_script, html)

def test_including_plotlyjs_false_div(self):
for include_plotlyjs in [False, 0, '']:
html = plotly.offline.plot(
fig,
include_plotlyjs=include_plotlyjs,
output_type='div')
self.assertNotIn(PLOTLYJS, html)
self.assertNotIn(cdn_script, html)
self.assertNotIn(directory_script, html)

def test_including_plotlyjs_cdn_html(self):
for include_plotlyjs in ['cdn', 'CDN', 'Cdn']:
html = self._read_html(plotly.offline.plot(
fig,
include_plotlyjs=include_plotlyjs,
output_type='file',
auto_open=False))
self.assertNotIn(PLOTLYJS, html)
self.assertIn(cdn_script, html)
self.assertNotIn(directory_script, html)

def test_including_plotlyjs_cdn_div(self):
for include_plotlyjs in ['cdn', 'CDN', 'Cdn']:
html = plotly.offline.plot(
fig,
include_plotlyjs=include_plotlyjs,
output_type='div')
self.assertNotIn(PLOTLYJS, html)
self.assertIn(cdn_script, html)
self.assertNotIn(directory_script, html)

def test_including_plotlyjs_directory_html(self):
self.assertFalse(os.path.exists('plotly.min.js'))

for include_plotlyjs in ['directory', 'Directory', 'DIRECTORY']:
html = self._read_html(plotly.offline.plot(
fig,
include_plotlyjs=include_plotlyjs,
auto_open=False))
self.assertNotIn(PLOTLYJS, html)
self.assertNotIn(cdn_script, html)
self.assertIn(directory_script, html)

# plot creates plotly.min.js in the output directory
self.assertTrue(os.path.exists('plotly.min.js'))
with open('plotly.min.js', 'r') as f:
self.assertEqual(f.read(), PLOTLYJS)

def test_including_plotlyjs_directory_div(self):
self.assertFalse(os.path.exists('plotly.min.js'))

for include_plotlyjs in ['directory', 'Directory', 'DIRECTORY']:
html = plotly.offline.plot(
fig,
include_plotlyjs=include_plotlyjs,
output_type='div',
auto_open=False)

self.assertNotIn(PLOTLYJS, html)
self.assertNotIn(cdn_script, html)
self.assertIn(directory_script, html)

# plot does NOT create a plotly.min.js file in the output directory
# when output_type is div
self.assertFalse(os.path.exists('plotly.min.js'))

def test_div_output(self):
html = plotly.offline.plot(fig, output_type='div', auto_open=False)
Expand Down Expand Up @@ -103,7 +210,7 @@ def test_autoresizing(self):
def test_autoresizing_div(self):

# If width or height wasn't specified, then we add a window resizer
for include_plotlyjs in [True, False]:
for include_plotlyjs in [True, False, 'cdn', 'directory']:
html = plotly.offline.plot(fig,
output_type='div',
include_plotlyjs=include_plotlyjs)
Expand Down

0 comments on commit 0cbcce7

Please sign in to comment.