Skip to content

Fix validate_colors and convert_colors_to_same_type modifying the colors passed to them - #5836

Merged
camdecoster merged 6 commits into
plotly:mainfrom
Enesuygurs:validate-colors-mutating-input
Oct 30, 2026
Merged

camdecoster merged 6 commits into
plotly:mainfrom
Enesuygurs:validate-colors-mutating-input

Conversation

@Enesuygurs

@Enesuygurs Enesuygurs commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Link to issue

Fixes #5846

Description of change

plotly.colors.validate_colors and plotly.colors.convert_colors_to_same_type rewrite the list they are given in place.

  • validate_colors: when called with None, it uses DEFAULT_PLOTLY_COLORS itself, so the module-level default colors become float tuples for the rest of the session. create_trisurf does this whenever colormap is left as None. When a user passes a colormap list instead, that list is converted to tuples.
  • convert_colors_to_same_type: it aliases a list argument the same way, so passing a built-in sequence rewrites it:
import plotly.colors as pc

pc.convert_colors_to_same_type(pc.sequential.Inferno, colortype="rgb")
pc.sequential.Inferno[:2]      # ['rgb(0, 0, 4)', 'rgb(27, 12, 65)'] instead of ['#000004', '#1b0c41']

pc.validate_colors(None)
pc.DEFAULT_PLOTLY_COLORS[:1]   # [(0.1215..., 0.4666..., 0.7058...)] instead of ['rgb(31, 119, 180)']

Both functions now copy the list before converting it. The return values are unchanged.

Testing strategy

I added test_color_conversions_do_not_modify_input. It checks that DEFAULT_PLOTLY_COLORS and a user list are unchanged after calling both helpers. It fails on main and passes with this change.

Locally, tests/test_core/test_colors, tests/test_plotly_utils and tests/test_optional/test_figure_factory pass. The exception is the create_ternary_contour tests: they need scikit-image, which isn't installed here, and they fail the same way on main. ruff format --check . passes.

Additional information (optional)

This also fixes cross-test pollution in the existing suite. test_sample_colorscale calls convert_colors_to_same_type(colors.sequential.Inferno, ...), which rewrote Inferno. As a result, running tests/test_core/test_colors before tests/test_plotly_utils made test_colorscale_validator.py::test_acceptance_named[Inferno_r] fail on main. It passes with this change.

Guidelines

…nput

Both helpers rewrote the list passed to them in place, which also
changed DEFAULT_PLOTLY_COLORS (e.g. after create_trisurf without a
colormap) and built-in sequences such as plotly.colors.sequential.Inferno.
Copy the list before converting it.
@KoolADE85

Copy link
Copy Markdown
Contributor

Thanks for the PR! Can you please create an issue that describes the problem and how you encountered this issue in your day-to-day usage of plotly.py?

@Enesuygurs

Copy link
Copy Markdown
Contributor Author

Thanks for taking a look! I opened #5846 with a description and a short reproduction, and linked it from this PR. To be transparent: I came across this while reading the color utilities rather than hitting it in a project. The most visible effect is that a built-in sequence such as plotly.colors.sequential.Inferno (or DEFAULT_PLOTLY_COLORS) stays modified for the rest of the session after one of these helpers is called on it. It also causes an order-dependent failure in the existing test suite.

@camdecoster camdecoster self-assigned this Oct 27, 2026

@camdecoster camdecoster left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix! Could you do a pass to see if there are any other similar mutations that need to be addressed? It looks like convert_dict_colors_to_same_type would need the same update (and maybe others).

Comment thread CHANGELOG.md Outdated
- 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 `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 `plotly.colors.validate_colors` and `plotly.colors.convert_colors_to_same_type` modifying the list of colors passed to them in place, which also changed `plotly.colors.DEFAULT_PLOTLY_COLORS` (for example after calling `create_trisurf` without a `colormap`) and built-in color sequences such as `plotly.colors.sequential.Inferno` [[#5836](https://github.com/plotly/plotly.py/pull/5836)], with thanks to @Enesuygurs for the contribution!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Fix `plotly.colors.validate_colors` and `plotly.colors.convert_colors_to_same_type` modifying the list of colors passed to them in place, which also changed `plotly.colors.DEFAULT_PLOTLY_COLORS` (for example after calling `create_trisurf` without a `colormap`) and built-in color sequences such as `plotly.colors.sequential.Inferno` [[#5836](https://github.com/plotly/plotly.py/pull/5836)], with thanks to @Enesuygurs 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!

@Enesuygurs

Copy link
Copy Markdown
Contributor Author

Thanks for the review! I went through every function in plotly.colors and found three more that changed their input in place: convert_dict_colors_to_same_type, validate_colors_dict and convert_colorscale_to_rgb. They now work on a copy as well. I added cases for them to test_color_conversions_do_not_modify_input, updated the changelog entry, and merged the latest main to resolve the changelog conflict.

@camdecoster camdecoster left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for getting this fixed!

@camdecoster
camdecoster merged commit 84498cc into plotly:main Oct 30, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: validate_colors and convert_colors_to_same_type modify the colors passed to them

3 participants