diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index 01fa090dc3a..07cb69719d9 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -9,7 +9,7 @@ from PIL import Image, ImageGrab -from .helper import assert_image_equal_tofile, skip_unless_feature +from .helper import assert_image_equal_tofile, on_ci, skip_unless_feature class TestImageGrab: @@ -60,12 +60,44 @@ def test_grab_invalid_xdisplay(self) -> None: ImageGrab.grab(xdisplay="error.test:0.0") assert str(e.value).startswith("X connection failed") - @pytest.mark.skipif(sys.platform != "win32", reason="Windows only") + @pytest.mark.skipif( + sys.platform != "darwin" or not on_ci(), reason="Only runs on macOS CI" + ) + def test_grab_handle(self) -> None: + p = subprocess.Popen( + [ + "osascript", + "-e", + 'tell application "Finder"\n' + 'open ("/" as POSIX file)\n' + "get id of front window\n" + "end tell", + ], + stdout=subprocess.PIPE, + ) + stdout = p.stdout + assert stdout is not None + window = int(stdout.read()) + + ImageGrab.grab(window=window) + + im = ImageGrab.grab((0, 0, 10, 10), window=window) + assert im.size == (10, 10) + + @pytest.mark.skipif( + sys.platform not in ("darwin", "win32"), reason="macOS and Windows only" + ) def test_grab_invalid_handle(self) -> None: - with pytest.raises(OSError, match="unable to get device context for handle"): - ImageGrab.grab(window=-1) - with pytest.raises(OSError, match="screen grab failed"): - ImageGrab.grab(window=0) + if sys.platform == "darwin": + with pytest.raises(subprocess.CalledProcessError): + ImageGrab.grab(window=-1) + else: + with pytest.raises( + OSError, match="unable to get device context for handle" + ): + ImageGrab.grab(window=-1) + with pytest.raises(OSError, match="screen grab failed"): + ImageGrab.grab(window=0) def test_grabclipboard(self) -> None: if sys.platform == "darwin": diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index 4228078b11b..66ee6dd33a3 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -43,25 +43,29 @@ def grab( fh, filepath = tempfile.mkstemp(".png") os.close(fh) args = ["screencapture"] - if window: + if window is not None: args += ["-l", str(window)] elif bbox: left, top, right, bottom = bbox args += ["-R", f"{left},{top},{right-left},{bottom-top}"] - subprocess.call(args + ["-x", filepath]) + args += ["-x", filepath] + retcode = subprocess.call(args) + if retcode: + raise subprocess.CalledProcessError(retcode, args) im = Image.open(filepath) im.load() os.unlink(filepath) if bbox: - if window: + if window is not None: # Determine if the window was in Retina mode or not # by capturing it without the shadow, # and checking how different the width is fh, filepath = tempfile.mkstemp(".png") os.close(fh) - subprocess.call( - ["screencapture", "-l", str(window), "-o", "-x", filepath] - ) + args = ["screencapture", "-l", str(window), "-o", "-x", filepath] + retcode = subprocess.call(args) + if retcode: + raise subprocess.CalledProcessError(retcode, args) with Image.open(filepath) as im_no_shadow: retina = im.width - im_no_shadow.width > 100 os.unlink(filepath) @@ -125,7 +129,10 @@ def grab( raise fh, filepath = tempfile.mkstemp(".png") os.close(fh) - subprocess.call(args + [filepath]) + args.append(filepath) + retcode = subprocess.call(args) + if retcode: + raise subprocess.CalledProcessError(retcode, args) im = Image.open(filepath) im.load() os.unlink(filepath)