Skip to content

Commit 8cff585

Browse files
authored
Merge branch 'main' into changes-for-main
2 parents 2d36a86 + 2d6771f commit 8cff585

File tree

4 files changed

+98
-5
lines changed

4 files changed

+98
-5
lines changed

CITATION.cff

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ authors:
99
- family-names: "Parmer"
1010
given-names: "Chris"
1111
title: "An interactive, open-source, and browser-based graphing library for Python"
12-
version: 5.24.1
12+
version: 6.3.0
1313
doi: 10.5281/zenodo.14503524
14-
date-released: 2024-09-12
14+
date-released: 2025-08-12
1515
url: "https://github.com/plotly/plotly.py"
16+

CONTRIBUTING.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,20 @@ you can install all packages with:
120120
pip install -e '.[dev]'
121121
```
122122

123+
If you're testing local changes in Jupyter Lab or Jupyter Notebook, you'll want to run these commands when you're setting up your development environment:
124+
```bash
125+
pip install jupyter
126+
jupyter labextension develop .
127+
```
128+
If you don't run that command, your figure will not render in the Jupyter Lab/ Jupyter Notebook editors.
129+
130+
If you're changing any of the code under the `js/` directory, you'll also want to run these commands:
131+
```
132+
cd js/
133+
npm ci
134+
npm run build
135+
```
136+
123137
These commands also create an *editable install* of plotly.py
124138
so that you can test your changes iteratively without having to rebuild the plotly.py package explicitly;
125139
for more information please see

plotly/io/_kaleido.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -376,13 +376,31 @@ def to_image(
376376
if defaults.mathjax:
377377
kopts["mathjax"] = defaults.mathjax
378378

379-
# TODO: Refactor to make it possible to use a shared Kaleido instance here
379+
width = (
380+
width
381+
or fig_dict.get("layout", {}).get("width")
382+
or fig_dict.get("layout", {})
383+
.get("template", {})
384+
.get("layout", {})
385+
.get("width")
386+
or defaults.default_width
387+
)
388+
height = (
389+
height
390+
or fig_dict.get("layout", {}).get("height")
391+
or fig_dict.get("layout", {})
392+
.get("template", {})
393+
.get("layout", {})
394+
.get("height")
395+
or defaults.default_height
396+
)
397+
380398
img_bytes = kaleido.calc_fig_sync(
381399
fig_dict,
382400
opts=dict(
383401
format=format or defaults.default_format,
384-
width=width or defaults.default_width,
385-
height=height or defaults.default_height,
402+
width=width,
403+
height=height,
386404
scale=scale or defaults.default_scale,
387405
),
388406
topojson=defaults.topojson,

tests/test_optional/test_kaleido/test_kaleido.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from pathlib import Path
55
import tempfile
66
from unittest.mock import patch
7+
import xml.etree.ElementTree as ET
78

89
from pdfrw import PdfReader
910
from PIL import Image
@@ -16,6 +17,26 @@
1617
fig = {"data": [], "layout": {"title": {"text": "figure title"}}}
1718

1819

20+
def create_figure(width=None, height=None):
21+
"""Create a simple figure with optional layout dimensions."""
22+
layout = {}
23+
if width:
24+
layout["width"] = width
25+
if height:
26+
layout["height"] = height
27+
28+
return go.Figure(data=[go.Scatter(x=[1, 2, 3], y=[1, 2, 3])], layout=layout)
29+
30+
31+
def parse_svg_dimensions(svg_bytes):
32+
"""Parse width and height from SVG bytes."""
33+
svg_str = svg_bytes.decode("utf-8")
34+
root = ET.fromstring(svg_str)
35+
width = root.get("width")
36+
height = root.get("height")
37+
return int(width) if width else None, int(height) if height else None
38+
39+
1940
def check_image(path_or_buffer, size=(700, 500), format="PNG"):
2041
if format == "PDF":
2142
img = PdfReader(path_or_buffer)
@@ -314,3 +335,42 @@ def test_get_chrome():
314335

315336
# Verify that kaleido.get_chrome_sync was called
316337
mock_get_chrome.assert_called_once()
338+
339+
340+
def test_width_height_priority():
341+
"""Test width/height priority: arguments > layout.width/height > defaults."""
342+
343+
# Test case 1: Arguments override layout
344+
fig = create_figure(width=800, height=600)
345+
svg_bytes = pio.to_image(fig, format="svg", width=1000, height=900)
346+
width, height = parse_svg_dimensions(svg_bytes)
347+
assert width == 1000 and height == 900, (
348+
"Arguments should override layout dimensions"
349+
)
350+
351+
# Test case 2: Layout dimensions used when no arguments
352+
fig = create_figure(width=800, height=600)
353+
svg_bytes = pio.to_image(fig, format="svg")
354+
width, height = parse_svg_dimensions(svg_bytes)
355+
assert width == 800 and height == 600, (
356+
"Layout dimensions should be used when no arguments provided"
357+
)
358+
359+
# Test case 3: Partial override (only width argument)
360+
fig = create_figure(width=800, height=600)
361+
svg_bytes = pio.to_image(fig, format="svg", width=1200)
362+
width, height = parse_svg_dimensions(svg_bytes)
363+
assert width == 1200 and height == 600, (
364+
"Width argument should override layout, height should use layout"
365+
)
366+
367+
# Test case 4: Defaults used when no layout or arguments
368+
fig = create_figure()
369+
svg_bytes = pio.to_image(fig, format="svg")
370+
width, height = parse_svg_dimensions(svg_bytes)
371+
assert width == pio.defaults.default_width, (
372+
"Default width should be used when no layout or argument"
373+
)
374+
assert height == pio.defaults.default_height, (
375+
"Default height should be used when no layout or argument"
376+
)

0 commit comments

Comments
 (0)