hex_to_rgb returns a tuple of the wrong length for a hex string that is neither 3 nor 6 digits, instead of rejecting it.
>>> from _plotly_utils.colors import hex_to_rgb
>>> hex_to_rgb("#12345")
(1, 2, 3, 4, 5)
The section width is computed as len(value) // 3 and the string is then sliced in steps of that width:
hex_total_length = len(value)
rgb_section_length = hex_total_length // 3
return tuple(
int(value[i : i + rgb_section_length], 16)
for i in range(0, hex_total_length, rgb_section_length)
)
For a 5-digit string that is 5 // 3 == 1, giving five 1-digit sections. A caller gets a 5-tuple where a colour is expected, and nothing downstream is checking the length.
Other lengths behave as badly in their own way: hex_to_rgb("") raises ValueError: range() arg 3 must not be zero, which does not say anything about colours.
The docstring already states the contract: "May be a full 6-character code or a 3-character shorthand code." Anything else should be rejected rather than silently reshaped.
Adjacent, and possibly worth a separate issue: n_colors(lowcolor, highcolor, 1) raises ZeroDivisionError: division by zero, because the increment is diff / (n_colors - 1). Asking for a single colour is a reasonable thing to do in a loop. I did not touch it because the right answer for n=1 is a judgement call: return [lowcolor], or raise something that names the argument. Happy to follow whichever you prefer.
Found with a round-trip and edge-case probe of _plotly_utils/colors. This issue was written with AI assistance (Claude Code).
hex_to_rgbreturns a tuple of the wrong length for a hex string that is neither 3 nor 6 digits, instead of rejecting it.The section width is computed as
len(value) // 3and the string is then sliced in steps of that width:For a 5-digit string that is
5 // 3 == 1, giving five 1-digit sections. A caller gets a 5-tuple where a colour is expected, and nothing downstream is checking the length.Other lengths behave as badly in their own way:
hex_to_rgb("")raisesValueError: range() arg 3 must not be zero, which does not say anything about colours.The docstring already states the contract: "May be a full 6-character code or a 3-character shorthand code." Anything else should be rejected rather than silently reshaped.
Adjacent, and possibly worth a separate issue:
n_colors(lowcolor, highcolor, 1)raisesZeroDivisionError: division by zero, because the increment isdiff / (n_colors - 1). Asking for a single colour is a reasonable thing to do in a loop. I did not touch it because the right answer forn=1is a judgement call: return[lowcolor], or raise something that names the argument. Happy to follow whichever you prefer.Found with a round-trip and edge-case probe of
_plotly_utils/colors. This issue was written with AI assistance (Claude Code).