Skip to content

Commit be0dbc5

Browse files
Merge pull request #2961 from adehad/lock_cdn_ver
Specify version of plotly.js in CDN URL
2 parents b0a5f77 + b4ecea0 commit be0dbc5

File tree

7 files changed

+105
-21
lines changed

7 files changed

+105
-21
lines changed

Diff for: CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
66
## [4.14.3] - 2021-01-12
77

88
### Fixed
9+
- Plotly.js cdn url will now be versioned by default for:
10+
`include_plotlyjs='cdn'` a new `include_plotlyjs='cdn-latest'` option
11+
has the original behaviour. Prevents likelihood of htmls generated with older
12+
`plotly.js` versions breaking with version bumps.
13+
[2961](https://github.com/plotly/plotly.py/pull/2961)
914

1015
- `px.timeline()` now allows `hover_data` formatting of start and end times [3018](https://github.com/plotly/plotly.py/pull/3018)
1116
- Small change to packaging of `plotlywidget` extension for JupyterLab 3 [3021](https://github.com/plotly/plotly.py/pull/3021)

Diff for: packages/python/plotly/plotly/io/_base_renderers.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
from os.path import isdir
88

99
import six
10-
from plotly.io import to_json, to_image, write_image, write_html
1110
from plotly import utils, optional_imports
11+
from plotly.io import to_json, to_image, write_image, write_html
1212
from plotly.io._orca import ensure_server
13+
from plotly.io._utils import plotly_cdn_url
1314
from plotly.offline.offline import _get_jconfig, get_plotlyjs
1415
from plotly.tools import return_figure_from_figure_or_data
1516

@@ -289,7 +290,7 @@ def activate(self):
289290
require.undef("plotly");
290291
requirejs.config({{
291292
paths: {{
292-
'plotly': ['https://cdn.plot.ly/plotly-latest.min']
293+
'plotly': ['{plotly_cdn}']
293294
}}
294295
}});
295296
require(['plotly'], function(Plotly) {{
@@ -298,7 +299,9 @@ def activate(self):
298299
}}
299300
</script>
300301
""".format(
301-
win_config=_window_plotly_config, mathjax_config=_mathjax_config
302+
win_config=_window_plotly_config,
303+
mathjax_config=_mathjax_config,
304+
plotly_cdn=plotly_cdn_url().rstrip(".js"),
302305
)
303306

304307
else:

Diff for: packages/python/plotly/plotly/io/_html.py

+27-12
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import six
77

88
from _plotly_utils.optional_imports import get_module
9-
from plotly.io._utils import validate_coerce_fig_to_dict
9+
from plotly.io._utils import validate_coerce_fig_to_dict, plotly_cdn_url
1010
from plotly.offline.offline import _get_jconfig, get_plotlyjs
1111
from plotly import utils
1212

@@ -61,10 +61,16 @@ def to_html(
6161
fully self-contained and can be used offline.
6262
6363
If 'cdn', a script tag that references the plotly.js CDN is included
64-
in the output. HTML files generated with this option are about 3MB
65-
smaller than those generated with include_plotlyjs=True, but they
66-
require an active internet connection in order to load the plotly.js
67-
library.
64+
in the output. The url used is versioned to match the bundled plotly.js.
65+
HTML files generated with this option are about 3MB smaller than those
66+
generated with include_plotlyjs=True, but they require an active
67+
internet connection in order to load the plotly.js library.
68+
69+
If 'cdn-latest', a script tag that always references the latest plotly.js
70+
CDN is included in the output.
71+
HTML files generated with this option are about 3MB smaller than those
72+
generated with include_plotlyjs=True, but they require an active
73+
internet connection in order to load the plotly.js library.
6874
6975
If 'directory', a script tag is included that references an external
7076
plotly.min.js bundle that is assumed to reside in the same
@@ -266,12 +272,15 @@ def to_html(
266272
require_start = 'require(["plotly"], function(Plotly) {'
267273
require_end = "});"
268274

269-
elif include_plotlyjs == "cdn":
275+
elif include_plotlyjs == "cdn" or include_plotlyjs == "cdn-latest":
276+
cdn_url = plotly_cdn_url()
277+
if include_plotlyjs == "cdn-latest":
278+
cdn_url = plotly_cdn_url(cdn_ver="latest")
270279
load_plotlyjs = """\
271280
{win_config}
272-
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>\
281+
<script src="{cdn_url}"></script>\
273282
""".format(
274-
win_config=_window_plotly_config
283+
win_config=_window_plotly_config, cdn_url=cdn_url
275284
)
276285

277286
elif include_plotlyjs == "directory":
@@ -417,10 +426,16 @@ def write_html(
417426
fully self-contained and can be used offline.
418427
419428
If 'cdn', a script tag that references the plotly.js CDN is included
420-
in the output. HTML files generated with this option are about 3MB
421-
smaller than those generated with include_plotlyjs=True, but they
422-
require an active internet connection in order to load the plotly.js
423-
library.
429+
in the output. The url used is versioned to match the bundled plotly.js.
430+
HTML files generated with this option are about 3MB smaller than those
431+
generated with include_plotlyjs=True, but they require an active
432+
internet connection in order to load the plotly.js library.
433+
434+
If 'cdn-latest', a script tag that always references the latest plotly.js
435+
CDN is included in the output.
436+
HTML files generated with this option are about 3MB smaller than those
437+
generated with include_plotlyjs=True, but they require an active
438+
internet connection in order to load the plotly.js library.
424439
425440
If 'directory', a script tag is included that references an external
426441
plotly.min.js bundle that is assumed to reside in the same

Diff for: packages/python/plotly/plotly/io/_utils.py

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import plotly
44
import plotly.graph_objs as go
5+
from plotly.offline import get_plotlyjs_version
56

67

78
def validate_coerce_fig_to_dict(fig, validate):
@@ -42,3 +43,8 @@ def validate_coerce_output_type(output_type):
4243
Must be one of: 'Figure', 'FigureWidget'"""
4344
)
4445
return cls
46+
47+
48+
def plotly_cdn_url(cdn_ver=get_plotlyjs_version()):
49+
"""Return a valid plotly CDN url."""
50+
return "https://cdn.plot.ly/plotly-{cdn_ver}.min.js".format(cdn_ver=cdn_ver,)

Diff for: packages/python/plotly/plotly/tests/test_core/test_offline/test_offline.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
import plotly
1414
import plotly.io as pio
15+
from plotly.io._utils import plotly_cdn_url
16+
1517
import json
1618

1719
packages_root = os.path.dirname(
@@ -39,7 +41,7 @@
3941
<script type="text/javascript">\
4042
window.PlotlyConfig = {MathJaxConfig: 'local'};</script>"""
4143

42-
cdn_script = '<script src="https://cdn.plot.ly/plotly-latest.min.js">' "</script>"
44+
cdn_script = '<script src="{cdn_url}"></script>'.format(cdn_url=plotly_cdn_url())
4345

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

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import sys
2+
3+
import pytest
4+
import numpy as np
5+
6+
7+
import plotly.graph_objs as go
8+
import plotly.io as pio
9+
from plotly.io._utils import plotly_cdn_url
10+
11+
12+
if sys.version_info >= (3, 3):
13+
import unittest.mock as mock
14+
from unittest.mock import MagicMock
15+
else:
16+
import mock
17+
from mock import MagicMock
18+
19+
# fixtures
20+
# --------
21+
@pytest.fixture
22+
def fig1(request):
23+
return go.Figure(
24+
data=[
25+
{
26+
"type": "scatter",
27+
"y": np.array([2, 1, 3, 2, 4, 2]),
28+
"marker": {"color": "green"},
29+
}
30+
],
31+
layout={"title": {"text": "Figure title"}},
32+
)
33+
34+
35+
# HTML
36+
# ----
37+
def assert_latest_cdn_connected(html):
38+
assert plotly_cdn_url(cdn_ver="latest") in html
39+
40+
41+
def assert_locked_version_cdn_connected(html):
42+
assert plotly_cdn_url() in html
43+
44+
45+
def test_latest_cdn_included(fig1):
46+
html_str = pio.to_html(fig1, include_plotlyjs="cdn-latest")
47+
assert_latest_cdn_connected(html_str)
48+
49+
50+
def test_versioned_cdn_included(fig1):
51+
html_str = pio.to_html(fig1, include_plotlyjs="cdn")
52+
assert_locked_version_cdn_connected(html_str)

Diff for: packages/python/plotly/plotly/tests/test_io/test_renderers.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import plotly.graph_objs as go
1212
import plotly.io as pio
1313
from plotly.offline import get_plotlyjs
14+
from plotly.io._utils import plotly_cdn_url
1415

1516
if sys.version_info >= (3, 3):
1617
import unittest.mock as mock
@@ -134,8 +135,8 @@ def assert_not_full_html(html):
134135
assert not html.startswith("<html")
135136

136137

137-
def assert_connected(html):
138-
assert "https://cdn.plot.ly/plotly-latest.min" in html
138+
def assert_html_renderer_connected(html):
139+
assert plotly_cdn_url().rstrip(".js") in html
139140

140141

141142
def assert_offline(html):
@@ -166,7 +167,7 @@ def test_colab_renderer_show(fig1):
166167
# Check html contents
167168
html = mock_arg1["text/html"]
168169
assert_full_html(html)
169-
assert_connected(html)
170+
assert_html_renderer_connected(html)
170171
assert_not_requirejs(html)
171172

172173
# check kwargs
@@ -195,7 +196,7 @@ def test_notebook_connected_show(fig1, name, connected):
195196
# Check init display contents
196197
bundle_display_html = mock_arg1_html
197198
if connected:
198-
assert_connected(bundle_display_html)
199+
assert_html_renderer_connected(bundle_display_html)
199200
else:
200201
assert_offline(bundle_display_html)
201202

@@ -306,7 +307,7 @@ def test_repr_html(renderer):
306307
template = (
307308
'<div> <script type="text/javascript">'
308309
"window.PlotlyConfig = {MathJaxConfig: 'local'};</script>\n "
309-
'<script src="https://cdn.plot.ly/plotly-latest.min.js"></script> '
310+
'<script src="' + plotly_cdn_url() + '"></script> '
310311
'<div id="cd462b94-79ce-42a2-887f-2650a761a144" class="plotly-graph-div" '
311312
'style="height:100%; width:100%;"></div> <script type="text/javascript">'
312313
" window.PLOTLYENV=window.PLOTLYENV || {};"

0 commit comments

Comments
 (0)