From 41c73d11b8ba6588b4ac02cf1f6b28d6c1619d71 Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Tue, 10 Feb 2026 00:18:18 -0800 Subject: [PATCH 1/2] Fix UnboundLocalError in _border for invalid tuple lengths and document rgba() color format The _border helper in ImageOps raised UnboundLocalError when given a tuple with a length other than 2 or 4 (e.g. 1-tuple or 3-tuple). This changes it to raise a clear ValueError instead. Also adds documentation for the rgba() color format in ImageColor, which was supported in code and tested but missing from the docs. Co-Authored-By: Claude Opus 4.6 --- Tests/test_imageops.py | 7 +++++++ docs/reference/ImageColor.rst | 4 ++++ src/PIL/ImageOps.py | 3 +++ 3 files changed, 14 insertions(+) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 35fe3bb8a57..5978707127e 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -256,6 +256,13 @@ def test_expand_palette(border: int | tuple[int, int, int, int]) -> None: assert_image_equal(im_cropped, im) +@pytest.mark.parametrize("border", ((1,), (1, 2, 3), (1, 2, 3, 4, 5))) +def test_expand_invalid_border(border: tuple[int, ...]) -> None: + with Image.open("Tests/images/hopper.ppm") as im: + with pytest.raises(ValueError): + ImageOps.expand(im, border) + + def test_colorize_2color() -> None: # Test the colorizing function with 2-color functionality diff --git a/docs/reference/ImageColor.rst b/docs/reference/ImageColor.rst index 68e228dba0f..82bd359bc99 100644 --- a/docs/reference/ImageColor.rst +++ b/docs/reference/ImageColor.rst @@ -27,6 +27,10 @@ The ImageColor module supports the following string formats: as three percentages (0% to 100%). For example, ``rgb(255,0,0)`` and ``rgb(100%,0%,0%)`` both specify pure red. +* RGBA functions, given as ``rgba(red, green, blue, alpha)`` where the color + values and the alpha value are integers in the range 0 to 255. For example, + ``rgba(255,0,0,128)`` specifies pure red with 50% opacity. + * Hue-Saturation-Lightness (HSL) functions, given as ``hsl(hue, saturation%, lightness%)`` where hue is the color given as an angle between 0 and 360 (red=0, green=120, blue=240), saturation is a value between 0% and 100% diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 42b10bd7bc8..8fdb9564d78 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -36,6 +36,9 @@ def _border(border: int | tuple[int, ...]) -> tuple[int, int, int, int]: left, top = right, bottom = border elif len(border) == 4: left, top, right, bottom = border + else: + msg = "border must be an integer or a 2- or 4-tuple" + raise ValueError(msg) else: left = top = right = bottom = border return left, top, right, bottom From 61ce55022a14a4ef8f9efa0dff7cb7296f94aa85 Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Tue, 10 Feb 2026 02:51:16 -0800 Subject: [PATCH 2/2] Simplify test to use Image.new instead of opening file --- Tests/test_imageops.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 5978707127e..31b7abecd60 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -258,9 +258,9 @@ def test_expand_palette(border: int | tuple[int, int, int, int]) -> None: @pytest.mark.parametrize("border", ((1,), (1, 2, 3), (1, 2, 3, 4, 5))) def test_expand_invalid_border(border: tuple[int, ...]) -> None: - with Image.open("Tests/images/hopper.ppm") as im: - with pytest.raises(ValueError): - ImageOps.expand(im, border) + im = Image.new("1", (1, 1)) + with pytest.raises(ValueError): + ImageOps.expand(im, border) def test_colorize_2color() -> None: