diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 8a9e82d99..6b237378d 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -698,28 +698,3 @@ def test_deprecation() -> None: assert ImageCms.VERSION == "1.0.0 pil" with pytest.warns(DeprecationWarning): assert isinstance(ImageCms.FLAGS, dict) - - -class TestGetOpenProfile: - def test_get_open_profile_with_string_path(self, tmp_path: Path) -> None: - # Create a dummy profile file - dummy_profile_path = tmp_path / "dummy.icc" - dummy_profile_path.write_bytes(b"Dummy ICC content") - - # Test with a string path - profile = ImageCms.getOpenProfile(str(dummy_profile_path)) - assert isinstance(profile, ImageCms.ImageCmsProfile) - - def test_get_open_profile_with_file_object(self, tmp_path: Path) -> None: - # Create a dummy profile file content - dummy_profile_content = b"Dummy ICC content" - dummy_profile_file = BytesIO(dummy_profile_content) - - # Test with a file-like object - profile = ImageCms.getOpenProfile(dummy_profile_file) - assert isinstance(profile, ImageCms.ImageCmsProfile) - - def test_get_open_profile_with_invalid_input(self) -> None: - # Test with invalid input - with pytest.raises(TypeError): - ImageCms.getOpenProfile(123) # Assuming passing an integer is invalid \ No newline at end of file diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index a4e2b5152..1d528b49f 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -215,13 +215,13 @@ class MockPyDecoder(ImageFile.PyDecoder): class MockPyEncoder(ImageFile.PyEncoder): - last = None # Add this line + last = None def __init__(self, mode: str, *args: Any) -> None: super().__init__(mode, *args) self._pushes_fd = False self.cleanup_called = False - MockPyEncoder.last = self # Update this line + MockPyEncoder.last = self def encode(self, buffer): # Simulate encoding diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py index 027ab74aa..ec296ca88 100644 --- a/Tests/test_imagetk.py +++ b/Tests/test_imagetk.py @@ -1,25 +1,23 @@ from __future__ import annotations +import gc import pytest +import tkinter as tk +from unittest import mock from PIL import Image +from PIL import ImageTk from .helper import assert_image_equal, hopper +TK_MODES = ("1", "L", "P", "RGB", "RGBA") + try: - import tkinter as tk - - from PIL import ImageTk - dir(ImageTk) HAS_TK = True except (OSError, ImportError): - # Skipped via pytestmark HAS_TK = False -TK_MODES = ("1", "L", "P", "RGB", "RGBA") - - pytestmark = pytest.mark.skipif(not HAS_TK, reason="Tk not installed") @@ -27,7 +25,6 @@ def setup_module() -> None: try: # setup tk tk.Frame() - # root = tk.Tk() except RuntimeError as v: pytest.skip(f"RuntimeError: {v}") except tk.TclError as v: @@ -102,3 +99,28 @@ def test_bitmapimage() -> None: # reloaded = ImageTk.getimage(im_tk) # assert_image_equal(reloaded, im) + + +def test_bitmapimage_del() -> None: + # Set up an image + im = Image.new("1", (10, 10)) + + # Mock the tkinter PhotoImage to track calls + with mock.patch.object(tk, 'BitmapImage', wraps=tk.BitmapImage) as mock_bitmapimage: + # Create an instance of BitmapImage + bitmap_image = ImageTk.BitmapImage(im) + + # Ensure the BitmapImage was created + assert mock_bitmapimage.call_count == 1 + + # Get the internal Tkinter image object + tk_image = bitmap_image._BitmapImage__photo + + # Mock the tk.call method to track the 'image delete' call + with mock.patch.object(tk_image.tk, 'call', wraps=tk_image.tk.call) as mock_tk_call: + # Delete the instance and force garbage collection + del bitmap_image + gc.collect() + + # Check that the 'image delete' command was called + mock_tk_call.assert_any_call("image", "delete", tk_image.name) diff --git a/conftest.py b/conftest.py index e61b5a270..72eb24dba 100644 --- a/conftest.py +++ b/conftest.py @@ -4,8 +4,7 @@ import sys from PIL import Image from PIL import PdfParser -from PIL import ImageCms -from PIL import McIdasImagePlugin +from PIL import ImageTk from PIL import ImageFile pytest_plugins = ["Tests.helper"] @@ -15,9 +14,8 @@ def calculate_coverage(test_name): all_branches = { "branches1": Image.branches, "branches2": PdfParser.XrefTable.branches, - "branches3": ImageCms.PyCMSError.branches, - "branches4": McIdasImagePlugin.McIdasImageFile.branches, - "branches5": ImageFile.PyEncoder.branches, + "branches3": ImageTk.BitmapImage.branches, + "branches4": ImageFile.PyEncoder.branches, # Add more } diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index 87176ae54..222428ed8 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -31,6 +31,10 @@ from io import BytesIO from . import Image +branches = { + "1": False, + "2": False, +} # -------------------------------------------------------------------- # Check for Tkinter interface hooks @@ -188,6 +192,10 @@ class PhotoImage: class BitmapImage: + branches = { + "1": False, + "2": False, +} """ A Tkinter-compatible bitmap image. This can be used everywhere Tkinter expects an image object. @@ -223,8 +231,10 @@ class BitmapImage: name = self.__photo.name self.__photo.name = None try: + BitmapImage.branches["1"] = True self.__photo.tk.call("image", "delete", name) except Exception: + BitmapImage pass # ignore internal errors def width(self) -> int: diff --git a/src/PIL/McIdasImagePlugin.py b/src/PIL/McIdasImagePlugin.py index 296e818ed..d7511b3bb 100644 --- a/src/PIL/McIdasImagePlugin.py +++ b/src/PIL/McIdasImagePlugin.py @@ -21,14 +21,6 @@ import struct from . import Image, ImageFile -branches = { - "1": False, - "2": False, - "3": False, - "4": False, - "5": False, - } - def _accept(prefix: bytes) -> bool: return prefix[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04" @@ -38,14 +30,6 @@ def _accept(prefix: bytes) -> bool: class McIdasImageFile(ImageFile.ImageFile): - branches = { - "1": False, - "2": False, - "3": False, - "4": False, - "5": False, - } - format = "MCIDAS" format_description = "McIdas area file" @@ -55,7 +39,6 @@ class McIdasImageFile(ImageFile.ImageFile): s = self.fp.read(256) if not _accept(s) or len(s) != 256: - McIdasImageFile.branches["1"] = True msg = "not an McIdas area file" raise SyntaxError(msg) @@ -64,20 +47,16 @@ class McIdasImageFile(ImageFile.ImageFile): # get mode if w[11] == 1: - McIdasImageFile.branches["2"] = True mode = rawmode = "L" elif w[11] == 2: - McIdasImageFile.branches["3"] = True # FIXME: add memory map support mode = "I" rawmode = "I;16B" elif w[11] == 4: - McIdasImageFile.branches["4"] = True # FIXME: add memory map support mode = "I" rawmode = "I;32B" else: - McIdasImageFile.branches["5"] = True msg = "unsupported McIdas format" raise SyntaxError(msg)