Skip to content

Commit f0a78a6

Browse files
committed
small io refactor to support kaleido v1
1 parent 82736ad commit f0a78a6

File tree

2 files changed

+89
-97
lines changed

2 files changed

+89
-97
lines changed

plotly/io/_kaleido.py

+88-96
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,41 @@
11
import os
22
import json
33
from pathlib import Path
4+
import importlib.metadata as importlib_metadata
5+
from packaging.version import Version
6+
import tempfile
7+
48
import plotly
59
from plotly.io._utils import validate_coerce_fig_to_dict
610

711
try:
8-
from kaleido.scopes.plotly import PlotlyScope
9-
10-
scope = PlotlyScope()
11-
12-
# Compute absolute path to the 'plotly/package_data/' directory
13-
root_dir = os.path.dirname(os.path.abspath(plotly.__file__))
14-
package_dir = os.path.join(root_dir, "package_data")
15-
scope.plotlyjs = os.path.join(package_dir, "plotly.min.js")
16-
if scope.mathjax is None:
17-
scope.mathjax = (
18-
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js"
19-
)
20-
except ImportError:
12+
import kaleido
13+
14+
kaleido_available = True
15+
kaleido_major = Version(importlib_metadata.version("kaleido")).major
16+
17+
if kaleido_major < 1:
18+
# Kaleido v0
19+
from kaleido.scopes.plotly import PlotlyScope
20+
21+
scope = PlotlyScope()
22+
# Compute absolute path to the 'plotly/package_data/' directory
23+
root_dir = os.path.dirname(os.path.abspath(plotly.__file__))
24+
package_dir = os.path.join(root_dir, "package_data")
25+
scope.plotlyjs = os.path.join(package_dir, "plotly.min.js")
26+
if scope.mathjax is None:
27+
scope.mathjax = (
28+
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js"
29+
)
30+
except ImportError as e:
31+
kaleido_available = False
32+
kaleido_major = -1
2133
PlotlyScope = None
2234
scope = None
2335

2436

2537
def to_image(
26-
fig, format=None, width=None, height=None, scale=None, validate=True, engine="auto"
38+
fig, format=None, width=None, height=None, scale=None, validate=True, engine=None
2739
):
2840
"""
2941
Convert a figure to a static image bytes string
@@ -35,100 +47,52 @@ def to_image(
3547
3648
format: str or None
3749
The desired image format. One of
38-
- 'png'
39-
- 'jpg' or 'jpeg'
40-
- 'webp'
41-
- 'svg'
42-
- 'pdf'
43-
- 'eps' (Requires the poppler library to be installed and on the PATH)
50+
- 'png'
51+
- 'jpg' or 'jpeg'
52+
- 'webp'
53+
- 'svg'
54+
- 'pdf'
55+
- 'eps' (Requires the poppler library to be installed and on the PATH)
4456
45-
If not specified, will default to:
46-
- `plotly.io.kaleido.scope.default_format` if engine is "kaleido"
47-
- `plotly.io.orca.config.default_format` if engine is "orca"
57+
If not specified, will default to `plotly.io.kaleido.scope.default_format`
4858
4959
width: int or None
5060
The width of the exported image in layout pixels. If the `scale`
5161
property is 1.0, this will also be the width of the exported image
5262
in physical pixels.
5363
54-
If not specified, will default to:
55-
- `plotly.io.kaleido.scope.default_width` if engine is "kaleido"
56-
- `plotly.io.orca.config.default_width` if engine is "orca"
64+
If not specified, will default to `plotly.io.kaleido.scope.default_width`
5765
5866
height: int or None
5967
The height of the exported image in layout pixels. If the `scale`
6068
property is 1.0, this will also be the height of the exported image
6169
in physical pixels.
6270
63-
If not specified, will default to:
64-
- `plotly.io.kaleido.scope.default_height` if engine is "kaleido"
65-
- `plotly.io.orca.config.default_height` if engine is "orca"
71+
If not specified, will default to `plotly.io.kaleido.scope.default_height`
6672
6773
scale: int or float or None
6874
The scale factor to use when exporting the figure. A scale factor
6975
larger than 1.0 will increase the image resolution with respect
7076
to the figure's layout pixel dimensions. Whereas as scale factor of
7177
less than 1.0 will decrease the image resolution.
7278
73-
If not specified, will default to:
74-
- `plotly.io.kaleido.scope.default_scale` if engine is "kaleido"
75-
- `plotly.io.orca.config.default_scale` if engine is "orca"
76-
79+
If not specified, will default to `plotly.io.kaleido.scope.default_scale`
7780
7881
validate: bool
7982
True if the figure should be validated before being converted to
8083
an image, False otherwise.
8184
82-
engine: str
83-
Image export engine to use:
84-
- "kaleido": Use Kaleido for image export
85-
- "orca": Use Orca for image export
86-
- "auto" (default): Use Kaleido if installed, otherwise use orca
85+
engine (deprecated): str
86+
No longer used. Kaleido is the only supported engine.
8787
8888
Returns
8989
-------
9090
bytes
9191
The image data
9292
"""
93-
# Handle engine
94-
# -------------
95-
if engine == "auto":
96-
if scope is not None:
97-
# Default to kaleido if available
98-
engine = "kaleido"
99-
else:
100-
# See if orca is available
101-
from ._orca import validate_executable
102-
103-
try:
104-
validate_executable()
105-
engine = "orca"
106-
except:
107-
# If orca not configured properly, make sure we display the error
108-
# message advising the installation of kaleido
109-
engine = "kaleido"
110-
111-
if engine == "orca":
112-
# Fall back to legacy orca image export path
113-
from ._orca import to_image as to_image_orca
114-
115-
return to_image_orca(
116-
fig,
117-
format=format,
118-
width=width,
119-
height=height,
120-
scale=scale,
121-
validate=validate,
122-
)
123-
elif engine != "kaleido":
124-
raise ValueError(
125-
"Invalid image export engine specified: {engine}".format(
126-
engine=repr(engine)
127-
)
128-
)
12993

13094
# Raise informative error message if Kaleido is not installed
131-
if scope is None:
95+
if not kaleido_available:
13296
raise ValueError(
13397
"""
13498
Image export using the "kaleido" engine requires the kaleido package,
@@ -137,12 +101,32 @@ def to_image(
137101
"""
138102
)
139103

140-
# Validate figure
141-
# ---------------
104+
# Convert figure to dict (and validate if requested)
142105
fig_dict = validate_coerce_fig_to_dict(fig, validate)
143-
img_bytes = scope.transform(
144-
fig_dict, format=format, width=width, height=height, scale=scale
145-
)
106+
107+
# Request image bytes
108+
if kaleido_major > 0:
109+
# Kaleido v1
110+
opts = {
111+
k: v
112+
for k, v in dict(
113+
format=format,
114+
width=width,
115+
height=height,
116+
scale=scale,
117+
).items()
118+
if v is not None
119+
}
120+
img_bytes = kaleido.calc_fig_sync(
121+
fig_dict,
122+
path=None,
123+
opts=opts,
124+
)
125+
else:
126+
# Kaleido v0
127+
img_bytes = scope.transform(
128+
fig_dict, format=format, width=width, height=height, scale=scale
129+
)
146130

147131
return img_bytes
148132

@@ -190,38 +174,29 @@ def write_image(
190174
property is 1.0, this will also be the width of the exported image
191175
in physical pixels.
192176
193-
If not specified, will default to:
194-
- `plotly.io.kaleido.scope.default_width` if engine is "kaleido"
195-
- `plotly.io.orca.config.default_width` if engine is "orca"
177+
If not specified, will default to`plotly.io.kaleido.scope.default_width`
196178
197179
height: int or None
198180
The height of the exported image in layout pixels. If the `scale`
199181
property is 1.0, this will also be the height of the exported image
200182
in physical pixels.
201183
202-
If not specified, will default to:
203-
- `plotly.io.kaleido.scope.default_height` if engine is "kaleido"
204-
- `plotly.io.orca.config.default_height` if engine is "orca"
184+
If not specified, will default to `plotly.io.kaleido.scope.default_height`
205185
206186
scale: int or float or None
207187
The scale factor to use when exporting the figure. A scale factor
208188
larger than 1.0 will increase the image resolution with respect
209189
to the figure's layout pixel dimensions. Whereas as scale factor of
210190
less than 1.0 will decrease the image resolution.
211191
212-
If not specified, will default to:
213-
- `plotly.io.kaleido.scope.default_scale` if engine is "kaleido"
214-
- `plotly.io.orca.config.default_scale` if engine is "orca"
192+
If not specified, will default to `plotly.io.kaleido.scope.default_scale`
215193
216194
validate: bool
217195
True if the figure should be validated before being converted to
218196
an image, False otherwise.
219197
220-
engine: str
221-
Image export engine to use:
222-
- "kaleido": Use Kaleido for image export
223-
- "orca": Use Orca for image export
224-
- "auto" (default): Use Kaleido if installed, otherwise use orca
198+
engine (deprecated): str
199+
No longer used. Kaleido is the only supported engine.
225200
226201
Returns
227202
-------
@@ -323,7 +298,7 @@ def full_figure_for_development(fig, warn=True, as_dict=False):
323298
"""
324299

325300
# Raise informative error message if Kaleido is not installed
326-
if scope is None:
301+
if not kaleido_available:
327302
raise ValueError(
328303
"""
329304
Full figure generation requires the kaleido package,
@@ -341,7 +316,24 @@ def full_figure_for_development(fig, warn=True, as_dict=False):
341316
"To suppress this warning, set warn=False"
342317
)
343318

344-
fig = json.loads(scope.transform(fig, format="json").decode("utf-8"))
319+
if kaleido_major > 0:
320+
# Kaleido v1
321+
try:
322+
json_file = Path(tempfile.mkstemp(suffix=".json")[1])
323+
kaleido.write_fig_sync(
324+
fig,
325+
json_file,
326+
dict(format="json"),
327+
)
328+
with open(json_file, "r") as f:
329+
fig = json.load(f)
330+
finally:
331+
# Cleanup: remove temp file
332+
json_file.unlink()
333+
else:
334+
# Kaleido v0
335+
fig = json.loads(scope.transform(fig, format="json").decode("utf-8"))
336+
345337
if as_dict:
346338
return fig
347339
else:

plotly/io/kaleido.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from ._kaleido import to_image, write_image, scope
1+
from ._kaleido import write_image, to_image

0 commit comments

Comments
 (0)