Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Fix concurrent first access to lazily initialized graph object properties, which could raise `ValueError("Invalid value")` [[#5691](https://github.com/plotly/plotly.py/pull/5691)], with thanks to @hb1915 for the contribution!
- Fix `mpl_to_plotly` crashing on touching bars (such as `plt.hist`) due to floating-point noise producing negative `bargap` values by clamping `bargap` to `[0, 1]` [[#5696](https://github.com/plotly/plotly.py/pull/5696)], with thanks to @robertoffmoura for the contribution!
- Fix `px.sunburst`, `px.treemap` and `px.icicle` listing sectors in a different order on every run when `path` is used with a Polars DataFrame; sectors now follow their order of first appearance for all dataframe backends [[#5766](https://github.com/plotly/plotly.py/pull/5766)], with thanks to @Irahan2 for the contribution!
- Fix `write_image`, `to_image` and `write_images` emitting `UserWarning: The kopts argument is ignored if using a server.` on every call under `kaleido.start_sync_server()` when no Kaleido options are set; the warning still appears when `plotly.io.defaults` sets `plotlyjs`, `mathjax` or custom `headers` [[#5787](https://github.com/plotly/plotly.py/pull/5787)], with thanks to @jayzhou2309 for the contribution!

## [7.1.0] - 2026-09-15

Expand Down
8 changes: 5 additions & 3 deletions plotly/io/_defaults.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Default settings for image generation

# This header is necessary to comply with Open Street Map tile policy:
# https://openstreetmap.github.io/owg-website/policies/tiles/#31-identification
DEFAULT_HEADERS = {"X-Requested-With": "plotly.py"}


class _Defaults(object):
"""
Expand All @@ -14,9 +18,7 @@ def __init__(self):
self.mathjax = None
self.topojson = None
self.plotlyjs = None
# This header is necessary to comply with Open Street Map tile policy:
# https://openstreetmap.github.io/owg-website/policies/tiles/#31-identification
self.headers = {"X-Requested-With": "plotly.py"}
self.headers = dict(DEFAULT_HEADERS)


defaults = _Defaults()
47 changes: 31 additions & 16 deletions plotly/io/_kaleido.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from contextlib import contextmanager
import os
import json
from pathlib import Path
Expand All @@ -8,7 +9,7 @@

import plotly
from plotly.io._utils import validate_coerce_fig_to_dict, broadcast_args_to_dicts
from plotly.io._defaults import defaults
from plotly.io._defaults import defaults, DEFAULT_HEADERS
from _plotly_utils.optional_imports import get_module

kaleido = get_module("kaleido", should_load=True)
Expand All @@ -35,6 +36,18 @@
_KALEIDO_AVAILABLE = None


@contextmanager
def _ignore_default_kopts_warning(kopts):
# A running kaleido sync server ignores kopts and warns about any. Only
# warn when the user set an option, not for plotly's default headers.
with warnings.catch_warnings():
if kopts == {"headers": DEFAULT_HEADERS}:
warnings.filterwarnings(
"ignore", message="The kopts argument", category=UserWarning
)
yield


def kaleido_available() -> bool:
"""
Returns True if Kaleido version 1.0.0 or greater is installed, otherwise False.
Expand Down Expand Up @@ -182,17 +195,18 @@ def to_image(
or defaults.default_height
)

img_bytes = kaleido.calc_fig_sync(
fig_dict,
opts=dict(
format=format or defaults.default_format,
width=width,
height=height,
scale=scale or defaults.default_scale,
),
topojson=defaults.topojson,
kopts=kopts,
)
with _ignore_default_kopts_warning(kopts):
img_bytes = kaleido.calc_fig_sync(
fig_dict,
opts=dict(
format=format or defaults.default_format,
width=width,
height=height,
scale=scale or defaults.default_scale,
),
topojson=defaults.topojson,
kopts=kopts,
)
except ChromeNotFoundError:
raise RuntimeError(PLOTLY_GET_CHROME_ERROR_MSG)

Expand Down Expand Up @@ -430,10 +444,11 @@ def write_images(
kopts["mathjax"] = defaults.mathjax
if defaults.headers:
kopts["headers"] = defaults.headers
kaleido.write_fig_from_object_sync(
kaleido_specs,
kopts=kopts,
)
with _ignore_default_kopts_warning(kopts):
kaleido.write_fig_from_object_sync(
kaleido_specs,
kopts=kopts,
)
except ChromeNotFoundError:
raise RuntimeError(PLOTLY_GET_CHROME_ERROR_MSG)

Expand Down
33 changes: 33 additions & 0 deletions tests/test_optional/test_kaleido/test_kaleido.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
from pathlib import Path
import tempfile
from unittest.mock import patch
import warnings
import xml.etree.ElementTree as ET

from pdfrw import PdfReader
from PIL import Image
import kaleido
import plotly.graph_objects as go
import plotly.io as pio

Expand Down Expand Up @@ -335,3 +337,34 @@ def test_width_height_priority():
assert height == pio.defaults.default_height, (
"Default height should be used when no layout or argument"
)


def kopts_warnings(export):
"""Run `export` under a kaleido sync server and return its kopts warnings."""
kaleido.start_sync_server()
try:
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
export()
finally:
kaleido.stop_sync_server()
return [str(w.message) for w in caught if "kopts" in str(w.message)]


def test_sync_server_no_kopts_warning_for_default_options(tmp_path):
def export():
pio.to_image(fig, format="svg")
pio.write_image(fig, tmp_path / "fig.png")
pio.write_images([fig], [tmp_path / "figs.png"])

assert kopts_warnings(export) == []


def test_sync_server_warns_when_options_are_ignored():
pio.defaults.plotlyjs = "https://cdn.plot.ly/plotly-3.0.0.js"
try:
warned = kopts_warnings(lambda: pio.to_image(fig, format="svg"))
finally:
pio.defaults.plotlyjs = None

assert warned == ["The kopts argument is ignored if using a server."]