diff --git a/CHANGELOG.md b/CHANGELOG.md index e52c392dc7..9e39b670b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +### Fixed +- Fix `hex_to_rgb` parsing of 3-digit shorthand hexadecimal colors such as `#FFF` [[#5662](https://github.com/plotly/plotly.py/pull/5662)], with thanks to @genrichez for the contribution! + ## [6.9.0] - 2026-07-09 diff --git a/_plotly_utils/colors/__init__.py b/_plotly_utils/colors/__init__.py index 254c8ce264..e3b5cfa320 100644 --- a/_plotly_utils/colors/__init__.py +++ b/_plotly_utils/colors/__init__.py @@ -752,11 +752,20 @@ def hex_to_rgb(value): """ Calculates rgb values from a hex color code. - :param (string) value: Hex color string + :param (string) value: Hex color string. May be a full 6-character code + or a 3-character shorthand code. :rtype (tuple) (r_value, g_value, b_value): tuple of rgb values + + Example: + + '#FFFFFF' --> (255, 255, 255) + '#FFF' --> (255, 255, 255) + """ value = value.lstrip("#") + if len(value) == 3: + value = "".join(c * 2 for c in value) hex_total_length = len(value) rgb_section_length = hex_total_length // 3 return tuple( diff --git a/plotly/matplotlylib/mpltools.py b/plotly/matplotlylib/mpltools.py index 4268136003..0a3206998b 100644 --- a/plotly/matplotlylib/mpltools.py +++ b/plotly/matplotlylib/mpltools.py @@ -10,6 +10,8 @@ import warnings import matplotlib.dates +from _plotly_utils.colors import hex_to_rgb + def check_bar_match(old_bar, new_bar): """Check if two bars belong in the same collection (bar chart). @@ -104,23 +106,6 @@ def convert_symbol(mpl_symbol): return "circle" # default -def hex_to_rgb(value): - """ - Change a hex color to an rgb tuple - - :param (str|unicode) value: The hex string we want to convert. - :return: (int, int, int) The red, green, blue int-tuple. - - Example: - - '#FFFFFF' --> (255, 255, 255) - - """ - value = value.lstrip("#") - lv = len(value) - return tuple(int(value[i : i + lv // 3], 16) for i in range(0, lv, lv // 3)) - - def merge_color_and_opacity(color, opacity): """ Merge hex color with an alpha (opacity) to get an rgba tuple. diff --git a/tests/test_plotly_utils/colors/test_color_conversions.py b/tests/test_plotly_utils/colors/test_color_conversions.py index 282a81ce56..6513b7c323 100644 --- a/tests/test_plotly_utils/colors/test_color_conversions.py +++ b/tests/test_plotly_utils/colors/test_color_conversions.py @@ -12,6 +12,15 @@ def test_hex_to_rgb_basic_values(): assert hex_to_rgb("#aabbcc") == (170, 187, 204) +def test_hex_to_rgb_shorthand_3_digit(): + assert hex_to_rgb("#fff") == (255, 255, 255) + assert hex_to_rgb("#000") == (0, 0, 0) + assert hex_to_rgb("#abc") == (170, 187, 204) + assert hex_to_rgb("#f00") == (255, 0, 0) + assert hex_to_rgb("#0f0") == (0, 255, 0) + assert hex_to_rgb("#00f") == (0, 0, 255) + + def test_label_rgb_formats_tuple(): assert label_rgb((255, 0, 0)) == "rgb(255, 0, 0)" assert label_rgb((1, 2, 3)) == "rgb(1, 2, 3)"