diff --git a/plotly/io/_defaults.py b/plotly/io/_defaults.py index a2a98884c9c..c36530c342d 100644 --- a/plotly/io/_defaults.py +++ b/plotly/io/_defaults.py @@ -13,6 +13,7 @@ def __init__(self): self.default_scale = 1 self.mathjax = None self.topojson = None + self.plotlyjs = None defaults = _Defaults() diff --git a/plotly/io/_kaleido.py b/plotly/io/_kaleido.py index fe997a29920..a76ad460286 100644 --- a/plotly/io/_kaleido.py +++ b/plotly/io/_kaleido.py @@ -369,6 +369,12 @@ def to_image( from kaleido.errors import ChromeNotFoundError try: + kopts = {} + if defaults.plotlyjs: + kopts["plotlyjs"] = defaults.plotlyjs + if defaults.mathjax: + kopts["mathjax"] = defaults.mathjax + # TODO: Refactor to make it possible to use a shared Kaleido instance here img_bytes = kaleido.calc_fig_sync( fig_dict, @@ -379,13 +385,7 @@ def to_image( scale=scale or defaults.default_scale, ), topojson=defaults.topojson, - kopts=( - dict( - mathjax=defaults.mathjax, - ) - if defaults.mathjax - else None - ), + kopts=kopts, ) except ChromeNotFoundError: raise RuntimeError(PLOTLY_GET_CHROME_ERROR_MSG) @@ -692,15 +692,14 @@ def write_images( from kaleido.errors import ChromeNotFoundError try: + kopts = {} + if defaults.plotlyjs: + kopts["plotlyjs"] = defaults.plotlyjs + if defaults.mathjax: + kopts["mathjax"] = defaults.mathjax kaleido.write_fig_from_object_sync( kaleido_specs, - kopts=( - dict( - mathjax=defaults.mathjax, - ) - if defaults.mathjax - else None - ), + kopts=kopts, ) except ChromeNotFoundError: raise RuntimeError(PLOTLY_GET_CHROME_ERROR_MSG) diff --git a/pyproject.toml b/pyproject.toml index 98f9876bca9..660d938fa5f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ dependencies = [ [project.optional-dependencies] express = ["numpy"] -kaleido = ["kaleido==1.0.0rc13"] +kaleido = ["kaleido==1.0.0rc15"] dev = ["black==25.1.0"] [project.scripts] diff --git a/test_requirements/requirements_optional.txt b/test_requirements/requirements_optional.txt index 7aad4821610..1c385d989bd 100644 --- a/test_requirements/requirements_optional.txt +++ b/test_requirements/requirements_optional.txt @@ -18,7 +18,7 @@ matplotlib scikit-image psutil # kaleido>=1.0.0 # Uncomment and delete line below once Kaleido v1 is released -kaleido==1.0.0rc13 +kaleido==1.0.0rc15 orjson polars[timezone] pyarrow diff --git a/tests/test_optional/test_kaleido/test_kaleido.py b/tests/test_optional/test_kaleido/test_kaleido.py index 84b07772307..47ac263ccfc 100644 --- a/tests/test_optional/test_kaleido/test_kaleido.py +++ b/tests/test_optional/test_kaleido/test_kaleido.py @@ -154,15 +154,105 @@ def test_bytesio(): def test_defaults(): """Test that image output defaults can be set using pio.defaults.*""" + test_fig = go.Figure(fig) + test_image_bytes = b"mock image data" + + # Check initial defaults + assert pio.defaults.default_format == "png" + assert pio.defaults.default_width == 700 + assert pio.defaults.default_height == 500 + assert pio.defaults.default_scale == 1 + assert pio.defaults.mathjax is None + assert pio.defaults.topojson is None + assert pio.defaults.plotlyjs is None + try: - assert pio.defaults.default_format == "png" + # Set new defaults pio.defaults.default_format = "svg" + pio.defaults.default_width = 701 + pio.defaults.default_height = 501 + pio.defaults.default_scale = 2 + pio.defaults.mathjax = ( + "https://cdn.jsdelivr.net/npm/mathjax@3.1.2/es5/tex-svg.js" + ) + pio.defaults.topojson = "path/to/topojson/files/" + pio.defaults.plotlyjs = "https://cdn.plot.ly/plotly-3.0.0.js" + + # Check that new defaults are saved assert pio.defaults.default_format == "svg" - result = pio.to_image(fig, format="svg", validate=False) + assert pio.defaults.default_width == 701 + assert pio.defaults.default_height == 501 + assert pio.defaults.default_scale == 2 + assert ( + pio.defaults.mathjax + == "https://cdn.jsdelivr.net/npm/mathjax@3.1.2/es5/tex-svg.js" + ) + assert pio.defaults.topojson == "path/to/topojson/files/" + assert pio.defaults.plotlyjs == "https://cdn.plot.ly/plotly-3.0.0.js" + + if kaleido_major() > 0: + # Check that all the defaults values are passed through to the function call to calc_fig_sync + with patch( + "plotly.io._kaleido.kaleido.calc_fig_sync", + return_value=test_image_bytes, + ) as mock_calc_fig: + result = pio.to_image(test_fig, validate=False) + + # Verify calc_fig_sync was called with correct args + # taken from pio.defaults + mock_calc_fig.assert_called_once() + args, kwargs = mock_calc_fig.call_args + assert args[0] == test_fig.to_dict() + assert kwargs["opts"]["format"] == "svg" + assert kwargs["opts"]["width"] == 701 + assert kwargs["opts"]["height"] == 501 + assert kwargs["opts"]["scale"] == 2 + assert kwargs["topojson"] == "path/to/topojson/files/" + # mathjax and plotlyjs are passed through in kopts + assert ( + kwargs["kopts"]["mathjax"] + == "https://cdn.jsdelivr.net/npm/mathjax@3.1.2/es5/tex-svg.js" + ) + assert ( + kwargs["kopts"]["plotlyjs"] == "https://cdn.plot.ly/plotly-3.0.0.js" + ) + + else: + # Check that all the default values have been set in pio._kaleido.scope + assert pio._kaleido.scope.default_format == "svg" + assert pio._kaleido.scope.default_width == 701 + assert pio._kaleido.scope.default_height == 501 + assert pio._kaleido.scope.default_scale == 2 + assert ( + pio._kaleido.scope.mathjax + == "https://cdn.jsdelivr.net/npm/mathjax@3.1.2/es5/tex-svg.js" + ) + assert pio._kaleido.scope.topojson == "path/to/topojson/files/" + assert pio._kaleido.scope.plotlyjs == "https://cdn.plot.ly/plotly-3.0.0.js" + + # Set topojson default back to None + # (otherwise image generation will fail) + pio.defaults.topojson = None + # Generate image for real and make sure it's an SVG + result = test_fig.to_image(format="svg", validate=False) assert result.startswith(b"