Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
f0a78a6
small io refactor to support kaleido v1
emilykl Mar 4, 2025
a681c84
update kaleido tests to support kaleido v1
emilykl Mar 4, 2025
a7a6f24
Merge branch 'main' into upgrade-kaleido
emilykl Mar 4, 2025
2e9c8af
update test requirements
emilykl Mar 4, 2025
60bb748
simplify to_image
emilykl Mar 4, 2025
b87f752
use k.calc_fig() instead of k.write_fig() in pio.full_figure_for_deve…
emilykl Mar 6, 2025
e203623
merge main
emilykl Mar 7, 2025
8054331
merge main
emilykl Mar 11, 2025
1a195a7
add ci job to test with kaleido v1
emilykl Mar 11, 2025
577d3ca
remove -y option
emilykl Mar 11, 2025
89209ad
run test_kaleido instead of test_io
emilykl Mar 11, 2025
96bf9a0
re-add orca, add deprecation warnings for orca and kaleido-v0 (exact …
emilykl Mar 13, 2025
350dd48
error message for 'eps' with kaleido v1
emilykl Mar 20, 2025
08c1d4e
pin kaleido v1 (prerelease) in test requirements
emilykl Mar 20, 2025
8b47a0a
format
emilykl Mar 21, 2025
249cc9f
deprecation warning wording
emilykl Mar 24, 2025
ad9dbd9
fix if/else for when engine not specified
emilykl Mar 24, 2025
e75a5df
add flow and endpoint for installing chrome via plotly
emilykl Mar 25, 2025
a2b4f3c
add write_images and to_images functions (not yet using shared Kaleid…
emilykl Mar 25, 2025
2549299
format
emilykl Mar 25, 2025
ab3b700
fix test_image_renderer() test to work with both kaleido v0 and v1
emilykl Mar 27, 2025
de473e1
support pos args in to_images and write_images; rename plotly_install…
emilykl Mar 27, 2025
71696fe
mising pos args in write_images()
emilykl Mar 27, 2025
100b955
add tests for write_images() and to_images()
emilykl Mar 27, 2025
5541a79
add new API for setting image generation defaults
emilykl Mar 27, 2025
95a05db
format
emilykl Mar 27, 2025
0a73d0e
Merge branch 'main' into upgrade-kaleido
emilykl Mar 27, 2025
4d3dd56
install kaleido 1.0.0rc11 from PyPI
emilykl Mar 28, 2025
d871e74
remove extra import
emilykl Mar 28, 2025
b3e8d36
add [kaleido] install extra to pyproject.toml to help with installing…
emilykl Mar 31, 2025
ef5f520
fix deprecation warnings
emilykl Mar 31, 2025
611e2e4
Merge branch 'main' into upgrade-kaleido
emilykl Mar 31, 2025
c92a1ee
small updates to deprecation warning text
emilykl Mar 31, 2025
b56d5ec
Merge branch 'upgrade-kaleido' of https://github.com/plotly/plotly.py…
emilykl Mar 31, 2025
c01cb8a
add missing changelog entries
emilykl Mar 31, 2025
bcd40f3
update changelog for 6.1.0b0
emilykl Mar 31, 2025
54985b8
update pyproject.toml for 6.1.0b0
emilykl Mar 31, 2025
b4af0d5
Merge pull request #5121 from plotly/release-6.1.0b0
emilykl Mar 31, 2025
0f61cc3
remove to_images tests
emilykl Apr 13, 2025
fe12aec
remove to_images function
emilykl Apr 13, 2025
692842f
refactor write_images to use kaleido write_fig function (WIP)
emilykl Apr 14, 2025
aac9c20
refactor write_images() to use kaleido.write_fig_from_object_sync()
emilykl Apr 14, 2025
ad57031
update kaleido tests
emilykl Apr 14, 2025
b9e5f7a
Merge branch 'main' into upgrade-kaleido
emilykl Apr 14, 2025
720ada5
add and fix type hints
emilykl Apr 14, 2025
53d486a
add mathjax and topojson config options
emilykl Apr 15, 2025
c990736
format
emilykl Apr 15, 2025
96bb17a
fix a few bugs with setting topojson default
emilykl Apr 24, 2025
f736f4c
remove 'import choreographer' and fix SyntaxWarning
emilykl Apr 24, 2025
aca1620
update kaleido rc version for tests
emilykl Apr 24, 2025
564aa51
clean up deprecation messages, fix bug with passing mathjax
emilykl Apr 28, 2025
2b45f47
add --path argument for plotly_get_chrome command
emilykl Apr 28, 2025
691fce6
format
emilykl Apr 28, 2025
0799bf5
merge main
emilykl Apr 28, 2025
651eafd
print exe path after installing Chrome
emilykl Apr 29, 2025
0413341
format
emilykl Apr 29, 2025
0c1b1c1
format
emilykl Apr 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add write_images and to_images functions (not yet using shared Kaleid…
…o so they are not faster
  • Loading branch information
emilykl committed Mar 25, 2025
commit a2b4f3c6801db5277798713fbd35e3d6b5b9b8a9
6 changes: 5 additions & 1 deletion plotly/io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import TYPE_CHECKING

if sys.version_info < (3, 7) or TYPE_CHECKING:
from ._kaleido import to_image, write_image, full_figure_for_development
from ._kaleido import to_image, write_image, to_images, write_images, full_figure_for_development
from . import orca, kaleido
from . import json
from ._json import to_json, from_json, read_json, write_json
Expand All @@ -15,6 +15,8 @@
__all__ = [
"to_image",
"write_image",
"to_images",
"write_images",
"orca",
"json",
"to_json",
Expand All @@ -37,6 +39,8 @@
[
"._kaleido.to_image",
"._kaleido.write_image",
"._kaleido.to_images",
"._kaleido.write_images",
"._kaleido.full_figure_for_development",
"._json.to_json",
"._json.from_json",
Expand Down
150 changes: 115 additions & 35 deletions plotly/io/_kaleido.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import warnings

import plotly
from plotly.io._utils import validate_coerce_fig_to_dict
from plotly.io._utils import validate_coerce_fig_to_dict, as_individual_kwargs

ENGINE_SUPPORT_TIMELINE = "September 2025"

Expand All @@ -29,6 +29,7 @@
scope.mathjax = (
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js"
)

except ImportError as e:
kaleido_available = False
kaleido_major = -1
Expand All @@ -37,7 +38,14 @@


def to_image(
fig, format=None, width=None, height=None, scale=None, validate=True, engine=None
fig,
format=None,
width=None,
height=None,
scale=None,
validate=True,
engine=None,
kaleido_instance=None,
):
"""
Convert a figure to a static image bytes string
Expand Down Expand Up @@ -87,6 +95,9 @@ def to_image(
engine (deprecated): str
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we print a deprecation warning if this argument is used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not yet, assuming we are in agreement about removing this argument, I'll add one

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm in agreement that we should remove this argument

No longer used. Kaleido is the only supported engine.

kaleido_instance: kaleido.Kaleido or None
An instance of the Kaleido class. If None, a new instance will be created.

Returns
-------
bytes
Expand Down Expand Up @@ -162,15 +173,17 @@ def to_image(
# Check if trying to export to EPS format, which is not supported in Kaleido v1
if format == "eps":
raise ValueError(
"""
EPS export is not supported with Kaleido v1.
Please downgrade to Kaleido v0 to use EPS export:
$ pip install kaleido==0.2.1
f"""
EPS export is not supported with Kaleido v1. Please use SVG or PDF instead.
You can also downgrade to Kaleido v0, but support for v0 will be removed after {ENGINE_SUPPORT_TIMELINE}.
To downgrade to Kaleido v0, run:
$ pip install kaleido<1.0.0
"""
)
import choreographer

try:
# TODO: Actually use provided kaleido_instance here
img_bytes = kaleido.calc_fig_sync(
fig_dict,
path=None,
Expand Down Expand Up @@ -204,35 +217,6 @@ def to_image(
return img_bytes


def install_chrome():
"""
Install Google Chrome for Kaleido
This function can be run from the command line using the command plotly_install_chrome
defined in pyproject.toml
"""
if not kaleido_available or kaleido_major < 1:
raise ValueError(
"This command requires Kaleido v1.0.0 or greater. Install it using `pip install kaleido`."
)
import choreographer
import sys

cli_yes = len(sys.argv) > 1 and sys.argv[1] == "-y"
if not cli_yes:
print(
"\nPlotly will install a copy of Google Chrome to be used for generating static images of plots.\n"
)
# TODO: Print path where Chrome will be installed
# print(f"Chrome will be installed at {chrome_download_path}\n")
response = input("Do you want to proceed? [y/n] ")
if not response or response[0].lower() != "y":
print("Cancelled")
return
print("Installing Chrome for Plotly...")
kaleido.get_chrome_sync()
print("Chrome installed successfully.")


def write_image(
fig,
file,
Expand All @@ -242,6 +226,7 @@ def write_image(
height=None,
validate=True,
engine="auto",
kaleido_instance=None,
):
"""
Convert a figure to a static image and write it to a file or writeable
Expand Down Expand Up @@ -300,6 +285,9 @@ def write_image(
engine (deprecated): str
No longer used. Kaleido is the only supported engine.

kaleido_instance: kaleido.Kaleido or None
An instance of the Kaleido class. If None, a new instance will be created.

Returns
-------
None
Expand Down Expand Up @@ -348,6 +336,7 @@ def write_image(
height=height,
validate=validate,
engine=engine,
kaleido_instance=kaleido_instance,
)

# Open file
Expand All @@ -373,6 +362,69 @@ def write_image(
path.write_bytes(img_data)


def to_images(**kwargs):
"""
Convert multiple figures to static images and return a list of image bytes

Parameters
----------
Accepts the same parameters as pio.to_image(), but any parameter may be either
a single value or a list of values. If more than one parameter is a list,
all must be the same length.

Returns
-------
list of bytes
The image data
"""
individual_kwargs = as_individual_kwargs(**kwargs)

if kaleido_available and kaleido_major > 0:
# Kaleido v1
# TODO: Use a single shared kaleido instance for all images
return [to_image(**kw) for kw in individual_kwargs]
else:
# Kaleido v0, or orca
return [to_image(**kw) for kw in individual_kwargs]


def write_images(**kwargs):
"""
Write multiple images to files or writeable objects. This is much faster than
calling write_image() multiple times.

Parameters
----------
Accepts the same parameters as pio.write_image(), but any parameter may be either
a single value or a list of values. If more than one parameter is a list,
all must be the same length.

Returns
-------
None
"""

if "file" not in kwargs:
raise ValueError("'file' argument is required")

# Get individual arguments, and separate out the 'file' argument
individual_kwargs = as_individual_kwargs(**kwargs)
files = [kw["file"] for kw in individual_kwargs]
individual_kwargs = [
{k: v for k, v in kw.items() if k != "file"} for kw in individual_kwargs
]

if kaleido_available and kaleido_major > 0:
# Kaleido v1
# TODO: Use a single shared kaleido instance for all images
for f, kw in zip(files, individual_kwargs):
write_image(file=f, **kw)
else:
# Kaleido v0, or orca
for f, kw in zip(files, individual_kwargs):
write_image(file=f, **kw)


def full_figure_for_development(fig, warn=True, as_dict=False):
"""
Compute default values for all attributes not specified in the input figure and
Expand Down Expand Up @@ -442,4 +494,32 @@ def full_figure_for_development(fig, warn=True, as_dict=False):
return go.Figure(fig, skip_invalid=True)


def install_chrome():
"""
Install Google Chrome for Kaleido
This function can be run from the command line using the command `plotly_install_chrome`
defined in pyproject.toml
"""
if not kaleido_available or kaleido_major < 1:
raise ValueError(
"This command requires Kaleido v1.0.0 or greater. Install it using `pip install kaleido`."
)
import sys

cli_yes = len(sys.argv) > 1 and sys.argv[1] == "-y"
if not cli_yes:
print(
"\nPlotly will install a copy of Google Chrome to be used for generating static images of plots.\n"
)
# TODO: Print path where Chrome will be installed
# print(f"Chrome will be installed at {chrome_download_path}\n")
response = input("Do you want to proceed? [y/n] ")
if not response or response[0].lower() != "y":
print("Cancelled")
return
print("Installing Chrome for Plotly...")
kaleido.get_chrome_sync()
print("Chrome installed successfully.")


__all__ = ["to_image", "write_image", "scope", "full_figure_for_development"]
36 changes: 36 additions & 0 deletions plotly/io/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,42 @@ def validate_coerce_output_type(output_type):
return cls


def as_individual_kwargs(**kwargs):
"""
Given one or more keyword arguments which may be either a single value or a list of values,
return a list of dictionaries where each dictionary has only single values for each keyword
by expanding the single values into lists.
If more than one keyword is a list, all lists must be the same length.

Parameters
----------
kwargs: dict
The keyword arguments

Returns
-------
list of dicts
A list of dictionaries
"""
# Check that all list arguments have the same length,
# and find out what that length is
# If there are no list arguments, length is 1
list_lengths = [len(v) for v in kwargs.values() if isinstance(v, list)]
if list_lengths and len(set(list_lengths)) > 1:
raise ValueError("All list arguments must have the same length.")
list_length = list_lengths[0] if list_lengths else 1

# Expand all arguments to lists of the same length
expanded_kwargs = {
k: [v] * list_length if not isinstance(v, list) else v
for k, v in kwargs.items()
}

# Reshape into a list of dictionaries
# Each dictionary represents the arguments for a single call to to_image
return [{k: v[i] for k, v in expanded_kwargs.items()} for i in range(list_length)]


def plotly_cdn_url(cdn_ver=get_plotlyjs_version()):
"""Return a valid plotly CDN url."""
return "https://cdn.plot.ly/plotly-{cdn_ver}.min.js".format(
Expand Down