Skip to content
Merged
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
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Fix `mpl_to_plotly` dropping matplotlib stairs plots by rendering `StepPatch` artists as step lines or filled step areas from their path vertices and edge style [[#5698](https://github.com/plotly/plotly.py/pull/5698)], with thanks to @robertoffmoura for the contribution!
- Fix `mpl_to_plotly` showing unwanted legends with placeholder or internal label entries (`_nolegend_`, `_childN`) by defaulting `layout.showlegend=False` unless the matplotlib figure explicitly defines a legend [[#5699](https://github.com/plotly/plotly.py/pull/5699)], with thanks to @robertoffmoura for the contribution!
- Fix `mpl_to_plotly` crashing on figures with fully transparent or alpha-carrying colors, such as boxplot outlier markers (`facecolor="none"`), rgba line colors, and `color="none"` text and axis labels; all matplotlib colors are now exported through a single helper that maps `"none"` to transparent rgba and keeps or overrides alpha as matplotlib does [[#5700](https://github.com/plotly/plotly.py/pull/5700)], with thanks to @robertoffmoura for the contribution!
- Fix serialization of box/lasso select data for figures with multiple subplots [[#5710](https://github.com/plotly/plotly.py/pull/5710)], with thanks to @flpi-sun for the contribution!
- Export tick label colors to the plotly tickfont in `mpl_to_plotly` [[#5716](https://github.com/plotly/plotly.py/pull/5716)], with thanks to @robertoffmoura for the contribution!
- Fix Plotly Express mutating lists passed to the `x` or `y` arguments in wide mode [[#5727](https://github.com/plotly/plotly.py/pull/5727)], with thanks to @cpruijsen for the contribution!
- Fix `convert_dash` mapping dotted lines (`linestyle=':'`) to invalid `'circle'` in Plotly, correctly converting them to `'dot'` and preserving custom dash patterns as pixel lists, [[#5719](https://github.com/plotly/plotly.py/pull/5719)] with thanks to @robertoffmoura for the contribution!
- Fix Plotly Express mutating lists passed to the `x` or `y` arguments in wide mode [[#5727](https://github.com/plotly/plotly.py/pull/5727)], with thanks to @cpruijsen 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 `mpl_to_plotly` conversion of matplotlib contour lines and line collections: close contour rings ending with `Z` codes, support dash styles, convert date x-axes, separate disjoint subpaths with `None` separators, hide line collection traces from the legend by default, and group consecutive same-style lines into single traces [[#5770](https://github.com/plotly/plotly.py/pull/5770)], with thanks to @robertoffmoura for the contribution!
- Fix serialization of box/lasso select data for figures with multiple subplots [[#5710](https://github.com/plotly/plotly.py/pull/5710)], with thanks to @flpi-sun for the contribution!
- Fix `plotly.io.write_images` ignoring `layout.width`/`layout.height` (and the template's) and exporting every figure at the default 700x500 size; it now uses the same width/height priority as `write_image` and `to_image` [[#5784](https://github.com/plotly/plotly.py/pull/5784)], with thanks to @breken-ai for the contribution!
- Fix color utilities mutating the list of colors passed to them [[#5836](https://github.com/plotly/plotly.py/pull/5836)], with thanks to @Enesuygurs for the contribution!


## [7.1.0] - 2026-09-15

Expand Down
17 changes: 16 additions & 1 deletion _plotly_utils/colors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,11 @@ def validate_colors(colors, colortype="tuple"):
else:
colors = list(colors)

else:
# copy the list so that neither the caller's list nor
# DEFAULT_PLOTLY_COLORS is modified in place below
colors = list(colors)

# convert color elements in list to tuple color
for j, each_color in enumerate(colors):
if "rgb" in each_color:
Expand Down Expand Up @@ -377,6 +382,9 @@ def validate_colors_dict(colors, colortype="tuple"):
"""
Validates dictionary of color(s)
"""
# copy the dict so that the caller's dict is not modified in place below
colors = dict(colors)

# validate each color element in the dictionary
for key in colors:
if "rgb" in colors[key]:
Expand Down Expand Up @@ -454,7 +462,7 @@ def convert_colors_to_same_type(
colors_list = list(colors)

elif isinstance(colors, list):
colors_list = colors
colors_list = list(colors)

# validate scale
if scale is not None:
Expand Down Expand Up @@ -498,6 +506,9 @@ def convert_dict_colors_to_same_type(colors_dict, colortype="rgb"):

:param (dict) colors_dict: a dictionary whose values are single colors
"""
# copy the dict so that the caller's dict is not modified in place below
colors_dict = dict(colors_dict)

for key in colors_dict:
if "#" in colors_dict[key]:
colors_dict[key] = color_parser(colors_dict[key], hex_to_rgb)
Expand Down Expand Up @@ -818,6 +829,10 @@ def convert_colorscale_to_rgb(colorscale):
converting a colorscale with tuple colors (each coordinate between 0
and 1) into a colorscale with the colors transformed into rgb colors
"""
# copy the colorscale and its [scale, color] pairs so that the caller's
# colorscale is not modified in place below
colorscale = [list(color) for color in colorscale]

for color in colorscale:
color[1] = convert_to_RGB_255(color[1])

Expand Down
26 changes: 26 additions & 0 deletions tests/test_core/test_colors/test_colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,32 @@ def test_convert_colors_to_same_type(self):
colortype,
)

def test_color_conversions_do_not_modify_input(self):
default_colors = list(colors.DEFAULT_PLOTLY_COLORS)

colors.validate_colors(None)
self.assertEqual(colors.DEFAULT_PLOTLY_COLORS, default_colors)

color_list = ["rgb(255, 0, 0)", "#0000ff"]
colors.validate_colors(color_list)
self.assertEqual(color_list, ["rgb(255, 0, 0)", "#0000ff"])

colors.convert_colors_to_same_type(color_list, colortype="tuple")
self.assertEqual(color_list, ["rgb(255, 0, 0)", "#0000ff"])

color_dict = {"a": "rgb(255, 0, 0)", "b": "#0000ff", "c": (0.5, 0.5, 0.5)}
expected_dict = dict(color_dict)

colors.validate_colors_dict(color_dict, colortype="rgb")
self.assertEqual(color_dict, expected_dict)

colors.convert_dict_colors_to_same_type(color_dict, colortype="tuple")
self.assertEqual(color_dict, expected_dict)

colorscale = [[0, (0.0, 0.0, 0.0)], [1, (1.0, 1.0, 1.0)]]
colors.convert_colorscale_to_rgb(colorscale)
self.assertEqual(colorscale, [[0, (0.0, 0.0, 0.0)], [1, (1.0, 1.0, 1.0)]])

def test_convert_dict_colors_to_same_type(self):
# test colortype
color_dict = dict(apple="rgb(1, 1, 1)")
Expand Down
Loading