From 667d26b38f3d41d1d9abc5937ca8d6479254f721 Mon Sep 17 00:00:00 2001 From: lllleolin-max <244385774+lllleolin-max@users.noreply.github.com> Date: Sat, 12 Sep 2026 14:59:16 +0800 Subject: [PATCH 1/3] Correct ImageOps.crop tuple border type annotation Document supported border tuple ordering and cover all border forms. Assisted-by: OpenAI GPT-6 --- Tests/test_imageops.py | 15 +++++++++++++++ docs/releasenotes/13.0.0.rst | 8 ++++++++ src/PIL/ImageOps.py | 14 +++++++++----- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index fd693bc522c..c7e63fbe548 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -87,6 +87,21 @@ def test_sanity() -> None: ImageOps.exif_transpose(hopper("RGB")) +@pytest.mark.parametrize( + "border, expected_box", + ( + (1, (1, 1, 127, 127)), + ((1, 2), (1, 2, 127, 126)), + ((1, 2, 3, 4), (1, 2, 125, 124)), + ), +) +def test_crop( + border: int | tuple[int, ...], expected_box: tuple[int, int, int, int] +) -> None: + im = hopper() + assert_image_equal(ImageOps.crop(im, border), im.crop(expected_box)) + + def test_1pxfit() -> None: # Division by zero in equalize if image is 1 pixel high newimg = ImageOps.fit(hopper("RGB").resize((1, 1)), (35, 35)) diff --git a/docs/releasenotes/13.0.0.rst b/docs/releasenotes/13.0.0.rst index 46dbed3b58b..85357926e36 100644 --- a/docs/releasenotes/13.0.0.rst +++ b/docs/releasenotes/13.0.0.rst @@ -126,6 +126,14 @@ Two new filters are available for :py:meth:`~PIL.Image.Image.resize` and Other changes ============= +ImageOps.crop border type annotation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``border`` type annotation for :py:func:`~PIL.ImageOps.crop` now includes +tuples, matching its existing support for two or four border widths. The tuple +ordering is also documented for :py:func:`~PIL.ImageOps.crop` and +:py:func:`~PIL.ImageOps.expand`. + Python 3.15 ^^^^^^^^^^^ diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 0141f57dd7d..f802bf25a9e 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -400,15 +400,17 @@ def pad( return out -def crop(image: Image.Image, border: int = 0) -> Image.Image: +def crop(image: Image.Image, border: int | tuple[int, ...] = 0) -> Image.Image: """ - Remove border from image. The same amount of pixels are removed - from all four sides. This function works on all image modes. + Remove border from image. This function works on all image modes. .. seealso:: :py:meth:`~PIL.Image.Image.crop` :param image: The image to crop. - :param border: The number of pixels to remove. + :param border: The number of pixels to remove. An integer removes the same + number of pixels from all four sides. A 2-tuple specifies the horizontal + and vertical borders. A 4-tuple specifies the left, top, right and bottom + borders. :return: An image. """ left, top, right, bottom = _border(border) @@ -516,7 +518,9 @@ def expand( Add border to the image :param image: The image to expand. - :param border: Border width, in pixels. + :param border: Border width, in pixels. An integer adds the same width to + all four sides. A 2-tuple specifies the horizontal and vertical borders. + A 4-tuple specifies the left, top, right and bottom borders. :param fill: Pixel fill value (a color value). Default is 0 (black). :return: An image. """ From e98a882a9df966effd2e85af007945cb34dfaaf4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 12 Sep 2026 18:23:54 +1000 Subject: [PATCH 2/3] Update docstrings --- src/PIL/ImageOps.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index f802bf25a9e..94964e12156 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -408,9 +408,10 @@ def crop(image: Image.Image, border: int | tuple[int, ...] = 0) -> Image.Image: :param image: The image to crop. :param border: The number of pixels to remove. An integer removes the same - number of pixels from all four sides. A 2-tuple specifies the horizontal - and vertical borders. A 4-tuple specifies the left, top, right and bottom - borders. + number of pixels from all four sides. A 2-tuple specifies + the number of pixels to remove horizontally and vertically. + A 4-tuple specifies the number of pixels to remove from the + left, top, right and bottom of the image. :return: An image. """ left, top, right, bottom = _border(border) @@ -519,8 +520,9 @@ def expand( :param image: The image to expand. :param border: Border width, in pixels. An integer adds the same width to - all four sides. A 2-tuple specifies the horizontal and vertical borders. - A 4-tuple specifies the left, top, right and bottom borders. + all four sides. A 2-tuple specifies the horizontal and + vertical border widths. A 4-tuple specifies the widths of + the left, top, right and bottom borders. :param fill: Pixel fill value (a color value). Default is 0 (black). :return: An image. """ From 4976f9f9923bf346ad26f1a9e43e2881b127d1a8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 12 Sep 2026 18:24:25 +1000 Subject: [PATCH 3/3] Do not mention in release notes --- docs/releasenotes/13.0.0.rst | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/releasenotes/13.0.0.rst b/docs/releasenotes/13.0.0.rst index 85357926e36..46dbed3b58b 100644 --- a/docs/releasenotes/13.0.0.rst +++ b/docs/releasenotes/13.0.0.rst @@ -126,14 +126,6 @@ Two new filters are available for :py:meth:`~PIL.Image.Image.resize` and Other changes ============= -ImageOps.crop border type annotation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The ``border`` type annotation for :py:func:`~PIL.ImageOps.crop` now includes -tuples, matching its existing support for two or four border widths. The tuple -ordering is also documented for :py:func:`~PIL.ImageOps.crop` and -:py:func:`~PIL.ImageOps.expand`. - Python 3.15 ^^^^^^^^^^^