-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support Kaleido v1 in Plotly.py #5062
base: main
Are you sure you want to change the base?
Changes from all commits
f0a78a6
a681c84
a7a6f24
2e9c8af
60bb748
b87f752
e203623
8054331
1a195a7
577d3ca
89209ad
96bf9a0
350dd48
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -117,6 +117,35 @@ commands: | |
source .venv/bin/activate | ||
python -m pytest -x test_init/test_lazy_imports.py | ||
|
||
test_io_kaleido_v1: | ||
steps: | ||
- checkout | ||
- browser-tools/install-chrome | ||
- browser-tools/install-chromedriver | ||
- run: | ||
name: Install dependencies | ||
command: | | ||
curl -LsSf https://astral.sh/uv/install.sh | sh | ||
uv venv | ||
source .venv/bin/activate | ||
uv pip install . | ||
uv pip install -r ./test_requirements/requirements_optional.txt | ||
# Install Kaleido v1 instead of the default version | ||
uv pip uninstall kaleido | ||
uv pip install 'git+https://github.com/plotly/[email protected]#subdirectory=src/py' | ||
- run: | ||
name: List installed packages and python version | ||
command: | | ||
source .venv/bin/activate | ||
uv pip list | ||
python --version | ||
- run: | ||
name: Test plotly.io image output with Kaleido v1 | ||
command: | | ||
source .venv/bin/activate | ||
python -m pytest tests/test_optional/test_kaleido | ||
no_output_timeout: 20m | ||
|
||
jobs: | ||
check-code-formatting: | ||
docker: | ||
|
@@ -166,6 +195,17 @@ jobs: | |
pandas_version: <<parameters.pandas_version>> | ||
numpy_version: <<parameters.numpy_version>> | ||
|
||
test_kaleido_v1: | ||
parameters: | ||
python_version: | ||
default: "3.12" | ||
type: string | ||
executor: | ||
name: docker-container | ||
python_version: <<parameters.python_version>> | ||
steps: | ||
- test_io_kaleido_v1 | ||
|
||
# Percy | ||
python_311_percy: | ||
docker: | ||
|
@@ -448,5 +488,10 @@ workflows: | |
python_version: "3.9" | ||
pandas_version: "1.2.4" | ||
numpy_version: "1.26.4" | ||
- test_kaleido_v1: | ||
matrix: | ||
parameters: | ||
python_version: | ||
- "3.12" | ||
- python_311_percy | ||
- build-doc |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,41 @@ | ||
import os | ||
import json | ||
from pathlib import Path | ||
import importlib.metadata as importlib_metadata | ||
from packaging.version import Version | ||
import warnings | ||
|
||
import plotly | ||
from plotly.io._utils import validate_coerce_fig_to_dict | ||
|
||
try: | ||
from kaleido.scopes.plotly import PlotlyScope | ||
|
||
scope = PlotlyScope() | ||
|
||
# Compute absolute path to the 'plotly/package_data/' directory | ||
root_dir = os.path.dirname(os.path.abspath(plotly.__file__)) | ||
package_dir = os.path.join(root_dir, "package_data") | ||
scope.plotlyjs = os.path.join(package_dir, "plotly.min.js") | ||
if scope.mathjax is None: | ||
scope.mathjax = ( | ||
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js" | ||
) | ||
except ImportError: | ||
import kaleido | ||
|
||
kaleido_available = True | ||
kaleido_major = Version(importlib_metadata.version("kaleido")).major | ||
|
||
if kaleido_major < 1: | ||
# Kaleido v0 | ||
from kaleido.scopes.plotly import PlotlyScope | ||
|
||
scope = PlotlyScope() | ||
# Compute absolute path to the 'plotly/package_data/' directory | ||
root_dir = os.path.dirname(os.path.abspath(plotly.__file__)) | ||
package_dir = os.path.join(root_dir, "package_data") | ||
scope.plotlyjs = os.path.join(package_dir, "plotly.min.js") | ||
if scope.mathjax is None: | ||
scope.mathjax = ( | ||
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js" | ||
) | ||
except ImportError as e: | ||
kaleido_available = False | ||
kaleido_major = -1 | ||
PlotlyScope = None | ||
scope = None | ||
|
||
|
||
def to_image( | ||
fig, format=None, width=None, height=None, scale=None, validate=True, engine="auto" | ||
fig, format=None, width=None, height=None, scale=None, validate=True, engine=None | ||
): | ||
""" | ||
Convert a figure to a static image bytes string | ||
|
@@ -35,65 +47,61 @@ def to_image( | |
|
||
format: str or None | ||
The desired image format. One of | ||
- 'png' | ||
- 'jpg' or 'jpeg' | ||
- 'webp' | ||
- 'svg' | ||
- 'pdf' | ||
- 'eps' (Requires the poppler library to be installed and on the PATH) | ||
- 'png' | ||
- 'jpg' or 'jpeg' | ||
- 'webp' | ||
- 'svg' | ||
- 'pdf' | ||
- 'eps' (Requires the poppler library to be installed and on the PATH) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we still handle EPS? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gvwilson Following up on this — Kaleido v1 does not support EPS yet. So either we drop support for EPS entirely, or document that EPS is only available with Kaleido v0 and add an informative error message. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please do the latter - thanks |
||
|
||
If not specified, will default to: | ||
- `plotly.io.kaleido.scope.default_format` if engine is "kaleido" | ||
- `plotly.io.orca.config.default_format` if engine is "orca" | ||
If not specified, will default to `plotly.io.kaleido.scope.default_format` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do these references to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, those docstrings should be changed -- I need to research where those defaults are defined in Kaleido v1. (@ayjayt do you know off the top of your head?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The order is png default overridden by user-specified output path w/ extension overriden by user-specified format in opts |
||
|
||
width: int or None | ||
The width of the exported image in layout pixels. If the `scale` | ||
property is 1.0, this will also be the width of the exported image | ||
in physical pixels. | ||
|
||
If not specified, will default to: | ||
- `plotly.io.kaleido.scope.default_width` if engine is "kaleido" | ||
- `plotly.io.orca.config.default_width` if engine is "orca" | ||
If not specified, will default to `plotly.io.kaleido.scope.default_width` | ||
|
||
height: int or None | ||
The height of the exported image in layout pixels. If the `scale` | ||
property is 1.0, this will also be the height of the exported image | ||
in physical pixels. | ||
|
||
If not specified, will default to: | ||
- `plotly.io.kaleido.scope.default_height` if engine is "kaleido" | ||
- `plotly.io.orca.config.default_height` if engine is "orca" | ||
If not specified, will default to `plotly.io.kaleido.scope.default_height` | ||
|
||
scale: int or float or None | ||
The scale factor to use when exporting the figure. A scale factor | ||
larger than 1.0 will increase the image resolution with respect | ||
to the figure's layout pixel dimensions. Whereas as scale factor of | ||
less than 1.0 will decrease the image resolution. | ||
|
||
If not specified, will default to: | ||
- `plotly.io.kaleido.scope.default_scale` if engine is "kaleido" | ||
- `plotly.io.orca.config.default_scale` if engine is "orca" | ||
|
||
If not specified, will default to `plotly.io.kaleido.scope.default_scale` | ||
|
||
validate: bool | ||
True if the figure should be validated before being converted to | ||
an image, False otherwise. | ||
|
||
engine: str | ||
Image export engine to use: | ||
- "kaleido": Use Kaleido for image export | ||
- "orca": Use Orca for image export | ||
- "auto" (default): Use Kaleido if installed, otherwise use orca | ||
engine (deprecated): str | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we print a deprecation warning if this argument is used? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
|
||
Returns | ||
------- | ||
bytes | ||
The image data | ||
""" | ||
|
||
# Handle engine | ||
# ------------- | ||
if engine is not None: | ||
warnings.warn( | ||
"The 'engine' parameter is deprecated and will be removed in a future version.", | ||
DeprecationWarning, | ||
) | ||
engine = "auto" | ||
|
||
if engine == "auto": | ||
if scope is not None: | ||
if kaleido_available: | ||
# Default to kaleido if available | ||
engine = "kaleido" | ||
else: | ||
|
@@ -109,6 +117,11 @@ def to_image( | |
engine = "kaleido" | ||
|
||
if engine == "orca": | ||
warnings.warn( | ||
"Support for the 'orca' engine is deprecated and will be removed in a future version. " | ||
"Please use the 'kaleido' engine instead.", | ||
DeprecationWarning, | ||
) | ||
# Fall back to legacy orca image export path | ||
from ._orca import to_image as to_image_orca | ||
|
||
|
@@ -128,7 +141,7 @@ def to_image( | |
) | ||
|
||
# Raise informative error message if Kaleido is not installed | ||
if scope is None: | ||
if not kaleido_available: | ||
raise ValueError( | ||
""" | ||
Image export using the "kaleido" engine requires the kaleido package, | ||
|
@@ -137,12 +150,41 @@ def to_image( | |
""" | ||
) | ||
|
||
# Validate figure | ||
# --------------- | ||
# Convert figure to dict (and validate if requested) | ||
fig_dict = validate_coerce_fig_to_dict(fig, validate) | ||
img_bytes = scope.transform( | ||
fig_dict, format=format, width=width, height=height, scale=scale | ||
) | ||
|
||
# Request image bytes | ||
if kaleido_major > 0: | ||
# Kaleido v1 | ||
# 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 | ||
""" | ||
) | ||
img_bytes = kaleido.calc_fig_sync( | ||
fig_dict, | ||
path=None, | ||
opts=dict( | ||
format=format, | ||
width=width, | ||
height=height, | ||
scale=scale, | ||
), | ||
) | ||
else: | ||
# Kaleido v0 | ||
warnings.warn( | ||
"Support for kaleido v0 is deprecated and will be removed in a future version. " | ||
"Please upgrade to kaleido v1 by running `pip install kaleido>=1.0.0`.", | ||
DeprecationWarning, | ||
) | ||
img_bytes = scope.transform( | ||
fig_dict, format=format, width=width, height=height, scale=scale | ||
) | ||
|
||
return img_bytes | ||
|
||
|
@@ -190,38 +232,29 @@ def write_image( | |
property is 1.0, this will also be the width of the exported image | ||
in physical pixels. | ||
|
||
If not specified, will default to: | ||
- `plotly.io.kaleido.scope.default_width` if engine is "kaleido" | ||
- `plotly.io.orca.config.default_width` if engine is "orca" | ||
If not specified, will default to`plotly.io.kaleido.scope.default_width` | ||
|
||
height: int or None | ||
The height of the exported image in layout pixels. If the `scale` | ||
property is 1.0, this will also be the height of the exported image | ||
in physical pixels. | ||
|
||
If not specified, will default to: | ||
- `plotly.io.kaleido.scope.default_height` if engine is "kaleido" | ||
- `plotly.io.orca.config.default_height` if engine is "orca" | ||
If not specified, will default to `plotly.io.kaleido.scope.default_height` | ||
|
||
scale: int or float or None | ||
The scale factor to use when exporting the figure. A scale factor | ||
larger than 1.0 will increase the image resolution with respect | ||
to the figure's layout pixel dimensions. Whereas as scale factor of | ||
less than 1.0 will decrease the image resolution. | ||
|
||
If not specified, will default to: | ||
- `plotly.io.kaleido.scope.default_scale` if engine is "kaleido" | ||
- `plotly.io.orca.config.default_scale` if engine is "orca" | ||
If not specified, will default to `plotly.io.kaleido.scope.default_scale` | ||
|
||
validate: bool | ||
True if the figure should be validated before being converted to | ||
an image, False otherwise. | ||
|
||
engine: str | ||
Image export engine to use: | ||
- "kaleido": Use Kaleido for image export | ||
- "orca": Use Orca for image export | ||
- "auto" (default): Use Kaleido if installed, otherwise use orca | ||
engine (deprecated): str | ||
No longer used. Kaleido is the only supported engine. | ||
|
||
Returns | ||
------- | ||
|
@@ -323,7 +356,7 @@ def full_figure_for_development(fig, warn=True, as_dict=False): | |
""" | ||
|
||
# Raise informative error message if Kaleido is not installed | ||
if scope is None: | ||
if not kaleido_available: | ||
raise ValueError( | ||
""" | ||
Full figure generation requires the kaleido package, | ||
|
@@ -341,7 +374,22 @@ def full_figure_for_development(fig, warn=True, as_dict=False): | |
"To suppress this warning, set warn=False" | ||
) | ||
|
||
fig = json.loads(scope.transform(fig, format="json").decode("utf-8")) | ||
if kaleido_major > 0: | ||
# Kaleido v1 | ||
bytes = kaleido.calc_fig_sync( | ||
fig, | ||
opts=dict(format="json"), | ||
) | ||
fig = json.loads(bytes.decode("utf-8")) | ||
else: | ||
# Kaleido v0 | ||
warnings.warn( | ||
"Support for kaleido v0 is deprecated and will be removed in a future version. " | ||
"Please upgrade to kaleido v1 by running `pip install kaleido>=1.0.0`.", | ||
DeprecationWarning, | ||
) | ||
fig = json.loads(scope.transform(fig, format="json").decode("utf-8")) | ||
|
||
if as_dict: | ||
return fig | ||
else: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
from ._kaleido import to_image, write_image, scope | ||
from ._kaleido import write_image, to_image |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,3 +23,4 @@ polars[timezone] | |
pyarrow | ||
plotly-geo | ||
vaex;python_version<="3.9" | ||
pdfrw |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uv pip install kaleido>=1.0.0 || uv pip install 'git+https://github.com/plotly/Kaleido.git@latest-tag#subdirectory=src/py'
is my recommendationThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 I'll just update the CI manually once Kaleido 1.0.0 full version is on PyPI.